| Name | agent-rails JSON |
| Version |
0.2.1
JSON |
| download |
| home_page | None |
| Summary | put your agents on guardrails |
| upload_time | 2025-09-03 03:13:45 |
| 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 | Lifecycle Orchestration for AI Agents
**Production-grade lifecycle orchestration for AI agents - monitor execution state and inject contextual guidance at critical moments**
Rails provides a framework-agnostic orchestration layer that creates a bidirectional communication channel between your agents and their lifecycle. Through a shared state store accessible to both Rails conditions and agent tools, Rails enables sophisticated feedback loops and intervention patterns.
```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, counter, state, queue
# Define a tool that accesses Rails
def fetch_data(data):
rails = current_rails() # Access Rails from within tools
rails.store.increment_sync('api_calls')
# Tool can push tasks to queues
if data.get('needs_processing'):
rails.store.push_queue_sync('tasks', data['item'])
return {"processed": True, "calls": rails.store.get_counter_sync('api_calls')}
async def main():
rails = Rails()
# Fluent condition builders with message injection
rails.add_rule(
condition=counter("errors") >= 2,
action=lambda msgs: msgs + [{
"role": "system",
"content": "Multiple errors detected. Switching to recovery mode."
}]
)
# Queue-based orchestration
rails.add_rule(
condition=queue("tasks").length > 5,
action=lambda msgs: msgs + [{
"role": "system",
"content": "Task queue is growing. Focus on completion before taking new tasks."
}]
)
# State-based guidance
rails.add_rule(
condition=state("mode") == "exploration",
action=lambda msgs: msgs + [{
"role": "system",
"content": "In exploration mode - be thorough but watch for diminishing returns."
}]
)
async with rails: # Context manager handles lifecycle
messages = [{"role": "user", "content": "Help me process data"}]
# Simulate tool calls and state changes
for i in range(6):
result = fetch_data({"item": i, "needs_processing": i > 2})
if i > 3:
await rails.store.increment('errors')
# Process messages through Rails conditions
from rails import Message, Role
rail_messages = [Message(role=Role(m["role"].upper()), content=m["content"]) for m in messages]
processed = await rails.process(rail_messages)
# Display any injected guidance
for msg in processed[len(rail_messages):]:
print(f"💬 Rails: {msg.content}")
asyncio.run(main())
```
## Architectural Principles
Rails implements a **bidirectional shared state model** that enables sophisticated lifecycle orchestration:
```
Agent Tools (Write State) ←→ Rails Store (Monitor State) ←→ Rails Conditions (Inject Context)
```
## Core Components
### 1. Fluent Condition Builders
Rails provides intuitive condition builders for common patterns:
```python
from rails import Rails, counter, state, queue
rails = Rails()
# Counter conditions with comparison operators
rails.add_rule(
condition=counter("api_calls") >= 10,
action=lambda msgs: msgs + [{"role": "system", "content": "API limit approaching"}]
)
# State conditions with equality checks
rails.add_rule(
condition=state("mode") == "production",
action=lambda msgs: msgs + [{"role": "system", "content": "In production - be careful"}]
)
# Queue conditions for task management
rails.add_rule(
condition=queue("errors").is_empty,
action=lambda msgs: msgs + [{"role": "system", "content": "All errors resolved!"}]
)
# Composite conditions
from rails import AndCondition, OrCondition, NotCondition
complex_condition = AndCondition(
counter("attempts") >= 3,
state("retry_enabled") == True
)
rails.add_rule(condition=complex_condition, action=retry_handler)
```
### 2. Shared State Store
The Rails store provides thread-safe state management accessible to both Rails and agent tools:
```python
from rails import Rails, current_rails
async with Rails() as rails:
# Counters - for tracking numeric values
await rails.store.increment("api_calls") # +1
await rails.store.increment("errors", 5) # +5
await rails.store.reset_counter("retries")
count = await rails.store.get_counter("api_calls")
# State values - for arbitrary data
await rails.store.set("user_tier", "premium")
await rails.store.set("config", {"debug": True, "timeout": 30})
tier = await rails.store.get("user_tier", default="standard")
# Queues - for task management (FIFO by default)
await rails.store.push_queue("tasks", "process_data")
await rails.store.push_queue("tasks", "generate_report")
task = await rails.store.pop_queue("tasks") # "process_data"
pending = await rails.store.queue_length("tasks") # 1
all_tasks = await rails.store.get_queue("tasks") # ["generate_report"]
# Synchronous versions for use in tools
rails.store.increment_sync("tool_calls")
rails.store.set_sync("last_tool", "calculator")
value = rails.store.get_sync("last_tool")
```
### 3. Tool Integration with `current_rails()`
Tools can access the Rails instance they're running within:
```python
from rails import current_rails
def data_processing_tool(data):
"""Tool that participates in lifecycle orchestration."""
rails = current_rails() # Get active Rails instance
# Track tool usage
rails.store.increment_sync('tool_calls')
rails.store.increment_sync(f'tool_calls_data_processing')
# Add tasks to queue for later processing
if data.get('requires_validation'):
rails.store.push_queue_sync('validation_queue', data['id'])
# Update state based on tool results
try:
result = process_data(data)
rails.store.increment_sync('successful_processing')
except Exception as e:
rails.store.increment_sync('processing_errors')
rails.store.push_queue_sync('error_log', str(e))
result = None
# Check if we should slow down
if rails.store.get_counter_sync('processing_errors') > 5:
rails.store.set_sync('mode', 'careful')
return result
# Tools automatically access Rails when called within Rails context
async with Rails() as rails:
# Tool can now use current_rails() to access the store
result = data_processing_tool({'data': 'value', 'requires_validation': True})
```
### 4. Message Injection System
Rails uses a functional approach to message transformation:
```python
from rails import Rails, Message, Role
from rails import AppendInjector, PrependInjector, ReplaceInjector
from rails import system, template
rails = Rails()
# Simple function-based injection
rails.add_rule(
condition=counter("errors") > 0,
action=lambda msgs: msgs + [Message(role=Role.SYSTEM, content="Error detected")]
)
# Using injector classes
error_injector = AppendInjector(
message=Message(role=Role.SYSTEM, content="Please review the errors")
)
rails.add_rule(
condition=counter("errors") >= 3,
action=error_injector.inject
)
# Factory functions for common patterns
rails.add_rule(
condition=state("mode") == "debug",
action=system("Debug mode active - verbose output enabled")
)
# Template injection with store values
rails.add_rule(
condition=state("user_name").exists,
action=template("Hello {user_name}, you have {api_calls} API calls remaining")
)
# Process messages through all rules
messages = [Message(role=Role.USER, content="Hello")]
processed = await rails.process(messages)
```
### 5. Event Streaming & Observability
Rails emits events for all state changes, enabling monitoring and debugging:
```python
from rails import Rails
rails = Rails()
# Subscribe to events
async def event_handler(event):
print(f"Event: {event.event_type} - {event.key} = {event.value}")
rails.store.subscribe_events(event_handler)
# All state changes emit events
await rails.store.increment("counter", triggered_by="user_action")
await rails.store.set("state", "active", triggered_by="system")
await rails.store.push_queue("tasks", "item", triggered_by="tool")
# Stream events for real-time monitoring
async for event in rails.store.event_stream():
if event.event_type == "counter_increment":
print(f"Counter {event.key} changed: {event.previous_value} → {event.value}")
# Get metrics snapshot
metrics = await rails.emit_metrics()
print(f"Active rules: {metrics['active_rules']}")
print(f"Store snapshot: {metrics['store_snapshot']}")
```
## Framework Integration
### Framework Adapters
Rails provides adapters for seamless integration with popular frameworks:
```python
from rails import Rails
from rails.adapters import create_adapter, BaseAdapter
# Generic adapter for any processing function
def my_agent(messages):
# Your agent logic here
return {"role": "assistant", "content": "Response"}
rails = Rails()
adapter = create_adapter(rails, my_agent)
async with adapter:
result = await adapter.process_messages(messages)
```
### LangChain Integration
```python
from rails import Rails, counter
from rails.adapters import LangChainAdapter
from langchain_openai import ChatOpenAI
rails = Rails()
# Add Rails conditions
rails.add_rule(
condition=counter("messages") >= 5,
action=lambda msgs: msgs + [{
"role": "system",
"content": "This conversation is getting long. Consider summarizing."
}]
)
# Create adapter
llm = ChatOpenAI(model="gpt-4")
adapter = LangChainAdapter(rails, llm)
# Rails processes messages before LangChain
result = await adapter.run(messages)
```
### Custom Framework Adapter
```python
from rails.adapters import BaseAdapter
class MyFrameworkAdapter(BaseAdapter):
def __init__(self, rails, agent):
super().__init__(rails)
self.agent = agent
async def process_messages(self, messages, **kwargs):
# Apply Rails processing
processed = await self.rails.process(messages)
# Convert to framework format
framework_messages = self.to_framework_format(processed)
# Process with framework
result = await self.agent.process(framework_messages)
# Update Rails state
await self.rails.store.increment("framework_calls")
return result
```
## Advanced Patterns
### Queue-Based Task Management
```python
from rails import Rails, current_rails, queue
rails = Rails()
# Tool adds tasks to queue
def task_manager_tool(action, task=None):
rails = current_rails()
if action == "add":
rails.store.push_queue_sync("tasks", task)
elif action == "complete":
completed = rails.store.pop_queue_sync("tasks")
rails.store.increment_sync("completed_tasks")
return completed
return rails.store.get_queue_sync("tasks")
# Rails monitors queue and provides guidance
rails.add_rule(
condition=queue("tasks").length > 5,
action=lambda msgs: msgs + [{
"role": "system",
"content": "Multiple tasks pending. Focus on completion before adding more."
}]
)
rails.add_rule(
condition=queue("tasks").is_empty & (counter("idle_turns") > 2),
action=lambda msgs: msgs + [{
"role": "system",
"content": "No pending tasks. Consider asking the user for next steps."
}]
)
```
### Error Recovery Pattern
```python
from rails import Rails, current_rails, counter
rails = Rails()
# Tool reports errors
def api_tool(endpoint):
rails = current_rails()
try:
result = call_api(endpoint)
rails.store.reset_counter_sync("consecutive_errors")
return result
except Exception as e:
rails.store.increment_sync("errors")
rails.store.increment_sync("consecutive_errors")
rails.store.push_queue_sync("error_log", {
"endpoint": endpoint,
"error": str(e),
"timestamp": datetime.now()
})
if rails.store.get_counter_sync("consecutive_errors") >= 3:
rails.store.set_sync("mode", "recovery")
return None
# Rails provides recovery guidance
rails.add_rule(
condition=state("mode") == "recovery",
action=lambda msgs: msgs + [{
"role": "system",
"content": "In recovery mode. Try alternative approaches or ask for help."
}]
)
```
### Progress Tracking
```python
from rails import Rails, current_rails
rails = Rails()
# Tools update progress
def step_tool(step_name, status):
rails = current_rails()
rails.store.set_sync(f"step_{step_name}", status)
if status == "complete":
rails.store.increment_sync("completed_steps")
total = rails.store.get_counter_sync("total_steps", 10)
completed = rails.store.get_counter_sync("completed_steps")
if completed == total:
rails.store.set_sync("workflow_status", "complete")
return {"step": step_name, "status": status}
# Rails provides progress updates
rails.add_rule(
condition=counter("completed_steps") % 5 == 0, # Every 5 steps
action=lambda msgs: msgs + [{
"role": "system",
"content": f"Good progress! {rails.store.get_counter_sync('completed_steps')} steps completed."
}]
)
```
## Configuration
### Store Configuration
```python
from rails import Rails, StoreConfig, QueueConfig
config = StoreConfig(
persist_on_exit=True,
persistence_path="./rails_state.json",
emit_events=True,
max_event_history=1000,
default_queues={
"tasks": QueueConfig(max_size=100, fifo=True, auto_dedup=True),
"errors": QueueConfig(max_size=50, fifo=False), # LIFO for errors
}
)
rails = Rails(store=Store(config=config))
```
### Middleware Stack
```python
from rails import Rails
rails = Rails()
# Add middleware for processing
async def logging_middleware(messages, store):
await store.increment("middleware_calls")
print(f"Processing {len(messages)} messages")
return messages
async def metric_middleware(messages, store):
start = time.time()
result = messages
duration = time.time() - start
await store.set("last_processing_time", duration)
return result
rails.add_middleware(logging_middleware)
rails.add_middleware(metric_middleware)
# Process through middleware stack
result = await rails.process_with_middleware(messages)
```
## 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 framework adapters
pip install agent-rails[dev] # includes development tools
```
### 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
```
## API Reference
### Rails
- `Rails()` - Create Rails instance
- `add_rule(condition, action, name=None, priority=0)` - Add orchestration rule
- `process(messages)` - Process messages through rules
- `process_with_middleware(messages)` - Process through middleware stack
- `add_middleware(middleware)` - Add middleware function
- `emit_metrics()` - Get metrics snapshot
### Store
- `increment(key, amount=1)` - Increment counter
- `get_counter(key, default=0)` - Get counter value
- `reset_counter(key)` - Reset counter to zero
- `set(key, value)` - Set state value
- `get(key, default=None)` - Get state value
- `delete(key)` - Delete state key
- `push_queue(queue, item)` - Add item to queue
- `pop_queue(queue)` - Remove and return item from queue
- `get_queue(queue)` - Get all queue items
- `queue_length(queue)` - Get queue length
- `clear_queue(queue)` - Clear all items from queue
- `get_snapshot()` - Get complete state snapshot
- `clear()` - Clear all state
### Conditions
- `counter(key)` - Create counter condition builder
- `state(key)` - Create state condition builder
- `queue(name)` - Create queue condition builder
- `AndCondition(*conditions)` - All conditions must be true
- `OrCondition(*conditions)` - Any condition must be true
- `NotCondition(condition)` - Negate condition
- `AlwaysCondition()` - Always true
- `NeverCondition()` - Always false
### Injectors
- `AppendInjector(message)` - Append message to end
- `PrependInjector(message)` - Prepend message to start
- `InsertInjector(message, index)` - Insert at index
- `ReplaceInjector(messages)` - Replace all messages
- `system(content, position="append")` - System message factory
- `template(template, role=Role.SYSTEM)` - Template message factory
---
**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/b6/22/8323b1d64389842c68386c71fef75d14305aedc2370f699fac2cb250532c/agent_rails-0.2.1.tar.gz",
"platform": null,
"description": "# Rails | Lifecycle Orchestration for AI Agents\n\n**Production-grade lifecycle orchestration for AI agents - monitor execution state and inject contextual guidance at critical moments**\n\nRails provides a framework-agnostic orchestration layer that creates a bidirectional communication channel between your agents and their lifecycle. Through a shared state store accessible to both Rails conditions and agent tools, Rails enables sophisticated feedback loops and intervention patterns.\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, counter, state, queue\n\n# Define a tool that accesses Rails\ndef fetch_data(data):\n rails = current_rails() # Access Rails from within tools\n rails.store.increment_sync('api_calls')\n \n # Tool can push tasks to queues\n if data.get('needs_processing'):\n rails.store.push_queue_sync('tasks', data['item'])\n \n return {\"processed\": True, \"calls\": rails.store.get_counter_sync('api_calls')}\n\nasync def main():\n rails = Rails()\n \n # Fluent condition builders with message injection\n rails.add_rule(\n condition=counter(\"errors\") >= 2,\n action=lambda msgs: msgs + [{\n \"role\": \"system\", \n \"content\": \"Multiple errors detected. Switching to recovery mode.\"\n }]\n )\n \n # Queue-based orchestration\n rails.add_rule(\n condition=queue(\"tasks\").length > 5,\n action=lambda msgs: msgs + [{\n \"role\": \"system\",\n \"content\": \"Task queue is growing. Focus on completion before taking new tasks.\"\n }]\n )\n \n # State-based guidance\n rails.add_rule(\n condition=state(\"mode\") == \"exploration\",\n action=lambda msgs: msgs + [{\n \"role\": \"system\",\n \"content\": \"In exploration mode - be thorough but watch for diminishing returns.\"\n }]\n )\n \n async with rails: # Context manager handles lifecycle\n messages = [{\"role\": \"user\", \"content\": \"Help me process data\"}]\n \n # Simulate tool calls and state changes\n for i in range(6):\n result = fetch_data({\"item\": i, \"needs_processing\": i > 2})\n if i > 3: \n await rails.store.increment('errors')\n \n # Process messages through Rails conditions\n from rails import Message, Role\n rail_messages = [Message(role=Role(m[\"role\"].upper()), content=m[\"content\"]) for m in messages]\n processed = await rails.process(rail_messages)\n \n # Display any injected guidance\n for msg in processed[len(rail_messages):]:\n print(f\"\ud83d\udcac Rails: {msg.content}\")\n\nasyncio.run(main())\n```\n\n## Architectural Principles\n\nRails implements a **bidirectional shared state model** that enables sophisticated lifecycle orchestration:\n\n```\nAgent Tools (Write State) \u2190\u2192 Rails Store (Monitor State) \u2190\u2192 Rails Conditions (Inject Context)\n```\n\n## Core Components\n\n### 1. Fluent Condition Builders\n\nRails provides intuitive condition builders for common patterns:\n\n```python\nfrom rails import Rails, counter, state, queue\n\nrails = Rails()\n\n# Counter conditions with comparison operators\nrails.add_rule(\n condition=counter(\"api_calls\") >= 10,\n action=lambda msgs: msgs + [{\"role\": \"system\", \"content\": \"API limit approaching\"}]\n)\n\n# State conditions with equality checks\nrails.add_rule(\n condition=state(\"mode\") == \"production\",\n action=lambda msgs: msgs + [{\"role\": \"system\", \"content\": \"In production - be careful\"}]\n)\n\n# Queue conditions for task management\nrails.add_rule(\n condition=queue(\"errors\").is_empty,\n action=lambda msgs: msgs + [{\"role\": \"system\", \"content\": \"All errors resolved!\"}]\n)\n\n# Composite conditions\nfrom rails import AndCondition, OrCondition, NotCondition\n\ncomplex_condition = AndCondition(\n counter(\"attempts\") >= 3,\n state(\"retry_enabled\") == True\n)\nrails.add_rule(condition=complex_condition, action=retry_handler)\n```\n\n### 2. Shared State Store\n\nThe Rails store provides thread-safe state management accessible to both Rails and agent tools:\n\n```python\nfrom rails import Rails, current_rails\n\nasync with Rails() as rails:\n # Counters - for tracking numeric values\n await rails.store.increment(\"api_calls\") # +1\n await rails.store.increment(\"errors\", 5) # +5\n await rails.store.reset_counter(\"retries\")\n count = await rails.store.get_counter(\"api_calls\")\n \n # State values - for arbitrary data\n await rails.store.set(\"user_tier\", \"premium\")\n await rails.store.set(\"config\", {\"debug\": True, \"timeout\": 30})\n tier = await rails.store.get(\"user_tier\", default=\"standard\")\n \n # Queues - for task management (FIFO by default)\n await rails.store.push_queue(\"tasks\", \"process_data\")\n await rails.store.push_queue(\"tasks\", \"generate_report\")\n task = await rails.store.pop_queue(\"tasks\") # \"process_data\"\n pending = await rails.store.queue_length(\"tasks\") # 1\n all_tasks = await rails.store.get_queue(\"tasks\") # [\"generate_report\"]\n \n # Synchronous versions for use in tools\n rails.store.increment_sync(\"tool_calls\")\n rails.store.set_sync(\"last_tool\", \"calculator\")\n value = rails.store.get_sync(\"last_tool\")\n```\n\n### 3. Tool Integration with `current_rails()`\n\nTools can access the Rails instance they're running within:\n\n```python\nfrom rails import current_rails\n\ndef data_processing_tool(data):\n \"\"\"Tool that participates in lifecycle orchestration.\"\"\"\n rails = current_rails() # Get active Rails instance\n \n # Track tool usage\n rails.store.increment_sync('tool_calls')\n rails.store.increment_sync(f'tool_calls_data_processing')\n \n # Add tasks to queue for later processing\n if data.get('requires_validation'):\n rails.store.push_queue_sync('validation_queue', data['id'])\n \n # Update state based on tool results\n try:\n result = process_data(data)\n rails.store.increment_sync('successful_processing')\n except Exception as e:\n rails.store.increment_sync('processing_errors')\n rails.store.push_queue_sync('error_log', str(e))\n result = None\n \n # Check if we should slow down\n if rails.store.get_counter_sync('processing_errors') > 5:\n rails.store.set_sync('mode', 'careful')\n \n return result\n\n# Tools automatically access Rails when called within Rails context\nasync with Rails() as rails:\n # Tool can now use current_rails() to access the store\n result = data_processing_tool({'data': 'value', 'requires_validation': True})\n```\n\n### 4. Message Injection System\n\nRails uses a functional approach to message transformation:\n\n```python\nfrom rails import Rails, Message, Role\nfrom rails import AppendInjector, PrependInjector, ReplaceInjector\nfrom rails import system, template\n\nrails = Rails()\n\n# Simple function-based injection\nrails.add_rule(\n condition=counter(\"errors\") > 0,\n action=lambda msgs: msgs + [Message(role=Role.SYSTEM, content=\"Error detected\")]\n)\n\n# Using injector classes\nerror_injector = AppendInjector(\n message=Message(role=Role.SYSTEM, content=\"Please review the errors\")\n)\nrails.add_rule(\n condition=counter(\"errors\") >= 3,\n action=error_injector.inject\n)\n\n# Factory functions for common patterns\nrails.add_rule(\n condition=state(\"mode\") == \"debug\",\n action=system(\"Debug mode active - verbose output enabled\")\n)\n\n# Template injection with store values\nrails.add_rule(\n condition=state(\"user_name\").exists,\n action=template(\"Hello {user_name}, you have {api_calls} API calls remaining\")\n)\n\n# Process messages through all rules\nmessages = [Message(role=Role.USER, content=\"Hello\")]\nprocessed = await rails.process(messages)\n```\n\n### 5. Event Streaming & Observability\n\nRails emits events for all state changes, enabling monitoring and debugging:\n\n```python\nfrom rails import Rails\n\nrails = Rails()\n\n# Subscribe to events\nasync def event_handler(event):\n print(f\"Event: {event.event_type} - {event.key} = {event.value}\")\n\nrails.store.subscribe_events(event_handler)\n\n# All state changes emit events\nawait rails.store.increment(\"counter\", triggered_by=\"user_action\")\nawait rails.store.set(\"state\", \"active\", triggered_by=\"system\")\nawait rails.store.push_queue(\"tasks\", \"item\", triggered_by=\"tool\")\n\n# Stream events for real-time monitoring\nasync for event in rails.store.event_stream():\n if event.event_type == \"counter_increment\":\n print(f\"Counter {event.key} changed: {event.previous_value} \u2192 {event.value}\")\n\n# Get metrics snapshot\nmetrics = await rails.emit_metrics()\nprint(f\"Active rules: {metrics['active_rules']}\")\nprint(f\"Store snapshot: {metrics['store_snapshot']}\")\n```\n\n## Framework Integration\n\n### Framework Adapters\n\nRails provides adapters for seamless integration with popular frameworks:\n\n```python\nfrom rails import Rails\nfrom rails.adapters import create_adapter, BaseAdapter\n\n# Generic adapter for any processing function\ndef my_agent(messages):\n # Your agent logic here\n return {\"role\": \"assistant\", \"content\": \"Response\"}\n\nrails = Rails()\nadapter = create_adapter(rails, my_agent)\n\nasync with adapter:\n result = await adapter.process_messages(messages)\n```\n\n### LangChain Integration\n\n```python\nfrom rails import Rails, counter\nfrom rails.adapters import LangChainAdapter\nfrom langchain_openai import ChatOpenAI\n\nrails = Rails()\n\n# Add Rails conditions\nrails.add_rule(\n condition=counter(\"messages\") >= 5,\n action=lambda msgs: msgs + [{\n \"role\": \"system\",\n \"content\": \"This conversation is getting long. Consider summarizing.\"\n }]\n)\n\n# Create adapter\nllm = ChatOpenAI(model=\"gpt-4\")\nadapter = LangChainAdapter(rails, llm)\n\n# Rails processes messages before LangChain\nresult = await adapter.run(messages)\n```\n\n### Custom Framework Adapter\n\n```python\nfrom rails.adapters import BaseAdapter\n\nclass MyFrameworkAdapter(BaseAdapter):\n def __init__(self, rails, agent):\n super().__init__(rails)\n self.agent = agent\n \n async def process_messages(self, messages, **kwargs):\n # Apply Rails processing\n processed = await self.rails.process(messages)\n \n # Convert to framework format\n framework_messages = self.to_framework_format(processed)\n \n # Process with framework\n result = await self.agent.process(framework_messages)\n \n # Update Rails state\n await self.rails.store.increment(\"framework_calls\")\n \n return result\n```\n\n## Advanced Patterns\n\n### Queue-Based Task Management\n\n```python\nfrom rails import Rails, current_rails, queue\n\nrails = Rails()\n\n# Tool adds tasks to queue\ndef task_manager_tool(action, task=None):\n rails = current_rails()\n \n if action == \"add\":\n rails.store.push_queue_sync(\"tasks\", task)\n elif action == \"complete\":\n completed = rails.store.pop_queue_sync(\"tasks\")\n rails.store.increment_sync(\"completed_tasks\")\n return completed\n \n return rails.store.get_queue_sync(\"tasks\")\n\n# Rails monitors queue and provides guidance\nrails.add_rule(\n condition=queue(\"tasks\").length > 5,\n action=lambda msgs: msgs + [{\n \"role\": \"system\",\n \"content\": \"Multiple tasks pending. Focus on completion before adding more.\"\n }]\n)\n\nrails.add_rule(\n condition=queue(\"tasks\").is_empty & (counter(\"idle_turns\") > 2),\n action=lambda msgs: msgs + [{\n \"role\": \"system\", \n \"content\": \"No pending tasks. Consider asking the user for next steps.\"\n }]\n)\n```\n\n### Error Recovery Pattern\n\n```python\nfrom rails import Rails, current_rails, counter\n\nrails = Rails()\n\n# Tool reports errors\ndef api_tool(endpoint):\n rails = current_rails()\n \n try:\n result = call_api(endpoint)\n rails.store.reset_counter_sync(\"consecutive_errors\")\n return result\n except Exception as e:\n rails.store.increment_sync(\"errors\")\n rails.store.increment_sync(\"consecutive_errors\")\n rails.store.push_queue_sync(\"error_log\", {\n \"endpoint\": endpoint,\n \"error\": str(e),\n \"timestamp\": datetime.now()\n })\n \n if rails.store.get_counter_sync(\"consecutive_errors\") >= 3:\n rails.store.set_sync(\"mode\", \"recovery\")\n \n return None\n\n# Rails provides recovery guidance\nrails.add_rule(\n condition=state(\"mode\") == \"recovery\",\n action=lambda msgs: msgs + [{\n \"role\": \"system\",\n \"content\": \"In recovery mode. Try alternative approaches or ask for help.\"\n }]\n)\n```\n\n### Progress Tracking\n\n```python\nfrom rails import Rails, current_rails\n\nrails = Rails()\n\n# Tools update progress\ndef step_tool(step_name, status):\n rails = current_rails()\n \n rails.store.set_sync(f\"step_{step_name}\", status)\n \n if status == \"complete\":\n rails.store.increment_sync(\"completed_steps\")\n total = rails.store.get_counter_sync(\"total_steps\", 10)\n completed = rails.store.get_counter_sync(\"completed_steps\")\n \n if completed == total:\n rails.store.set_sync(\"workflow_status\", \"complete\")\n \n return {\"step\": step_name, \"status\": status}\n\n# Rails provides progress updates\nrails.add_rule(\n condition=counter(\"completed_steps\") % 5 == 0, # Every 5 steps\n action=lambda msgs: msgs + [{\n \"role\": \"system\",\n \"content\": f\"Good progress! {rails.store.get_counter_sync('completed_steps')} steps completed.\"\n }]\n)\n```\n\n## Configuration\n\n### Store Configuration\n\n```python\nfrom rails import Rails, StoreConfig, QueueConfig\n\nconfig = StoreConfig(\n persist_on_exit=True,\n persistence_path=\"./rails_state.json\",\n emit_events=True,\n max_event_history=1000,\n default_queues={\n \"tasks\": QueueConfig(max_size=100, fifo=True, auto_dedup=True),\n \"errors\": QueueConfig(max_size=50, fifo=False), # LIFO for errors\n }\n)\n\nrails = Rails(store=Store(config=config))\n```\n\n### Middleware Stack\n\n```python\nfrom rails import Rails\n\nrails = Rails()\n\n# Add middleware for processing\nasync def logging_middleware(messages, store):\n await store.increment(\"middleware_calls\")\n print(f\"Processing {len(messages)} messages\")\n return messages\n\nasync def metric_middleware(messages, store):\n start = time.time()\n result = messages\n duration = time.time() - start\n await store.set(\"last_processing_time\", duration)\n return result\n\nrails.add_middleware(logging_middleware)\nrails.add_middleware(metric_middleware)\n\n# Process through middleware stack\nresult = await rails.process_with_middleware(messages)\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 framework adapters\npip install agent-rails[dev] # includes development tools\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## API Reference\n\n### Rails\n\n- `Rails()` - Create Rails instance\n- `add_rule(condition, action, name=None, priority=0)` - Add orchestration rule\n- `process(messages)` - Process messages through rules\n- `process_with_middleware(messages)` - Process through middleware stack\n- `add_middleware(middleware)` - Add middleware function\n- `emit_metrics()` - Get metrics snapshot\n\n### Store\n\n- `increment(key, amount=1)` - Increment counter\n- `get_counter(key, default=0)` - Get counter value\n- `reset_counter(key)` - Reset counter to zero\n- `set(key, value)` - Set state value\n- `get(key, default=None)` - Get state value\n- `delete(key)` - Delete state key\n- `push_queue(queue, item)` - Add item to queue\n- `pop_queue(queue)` - Remove and return item from queue\n- `get_queue(queue)` - Get all queue items\n- `queue_length(queue)` - Get queue length\n- `clear_queue(queue)` - Clear all items from queue\n- `get_snapshot()` - Get complete state snapshot\n- `clear()` - Clear all state\n\n### Conditions\n\n- `counter(key)` - Create counter condition builder\n- `state(key)` - Create state condition builder \n- `queue(name)` - Create queue condition builder\n- `AndCondition(*conditions)` - All conditions must be true\n- `OrCondition(*conditions)` - Any condition must be true\n- `NotCondition(condition)` - Negate condition\n- `AlwaysCondition()` - Always true\n- `NeverCondition()` - Always false\n\n### Injectors\n\n- `AppendInjector(message)` - Append message to end\n- `PrependInjector(message)` - Prepend message to start\n- `InsertInjector(message, index)` - Insert at index\n- `ReplaceInjector(messages)` - Replace all messages\n- `system(content, position=\"append\")` - System message factory\n- `template(template, role=Role.SYSTEM)` - Template message factory\n\n---\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.2.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": "52cd68595de1e5c1cdbf02f936454e9a1f5fddb66d717156fc0fe2f1531f13c9",
"md5": "d7db6eec444b3301b5aff6036e7e0449",
"sha256": "ec9271c7f91aeaf1bebc27e6b202f953b6e8f05a5d2068178a4bfc210c589af3"
},
"downloads": -1,
"filename": "agent_rails-0.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d7db6eec444b3301b5aff6036e7e0449",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 38820,
"upload_time": "2025-09-03T03:13:44",
"upload_time_iso_8601": "2025-09-03T03:13:44.053573Z",
"url": "https://files.pythonhosted.org/packages/52/cd/68595de1e5c1cdbf02f936454e9a1f5fddb66d717156fc0fe2f1531f13c9/agent_rails-0.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b6228323b1d64389842c68386c71fef75d14305aedc2370f699fac2cb250532c",
"md5": "95447883e00ba235c79a87667893ca12",
"sha256": "54b629f9b7004085925ca8aaf1e30508eacc79aa9159f1adada23d67e879ef86"
},
"downloads": -1,
"filename": "agent_rails-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "95447883e00ba235c79a87667893ca12",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 39969,
"upload_time": "2025-09-03T03:13:45",
"upload_time_iso_8601": "2025-09-03T03:13:45.313693Z",
"url": "https://files.pythonhosted.org/packages/b6/22/8323b1d64389842c68386c71fef75d14305aedc2370f699fac2cb250532c/agent_rails-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-03 03:13:45",
"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"
}