> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praison.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Recipe Serve Advanced Features

> Rate limiting, metrics, admin endpoints, workers, and OpenTelemetry

# Recipe Serve Advanced Features

This guide covers advanced server features including rate limiting, metrics, admin endpoints, workers, and OpenTelemetry tracing.

## Rate Limiting

Protect your server from abuse with configurable rate limiting.

### Configuration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import create_app, serve

# Create app with rate limiting
app = create_app(config={
    "rate_limit": 100,  # 100 requests per minute per client
    "rate_limit_exempt_paths": ["/health", "/metrics"]
})

# Or via serve()
serve(
    host="127.0.0.1",
    port=8765,
    config={"rate_limit": 100}
)
```

### Rate Limiter Class

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import create_rate_limiter, RateLimiter

# Create rate limiter
limiter = create_rate_limiter(requests_per_minute=100)

# Check if request is allowed
allowed, retry_after = limiter.check("client-ip-or-key")

if not allowed:
    print(f"Rate limited. Retry after {retry_after} seconds")
```

### Response Format

When rate limited, clients receive:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests"
  }
}
```

With headers:

* `Retry-After: <seconds>`

## Request Size Limits

Prevent oversized payloads from overwhelming your server.

### Configuration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import create_app, DEFAULT_MAX_REQUEST_SIZE

# Default is 10MB
print(f"Default max size: {DEFAULT_MAX_REQUEST_SIZE} bytes")

# Custom size limit
app = create_app(config={
    "max_request_size": 5 * 1024 * 1024  # 5MB
})
```

### Response Format

When request is too large:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "error": {
    "code": "request_too_large",
    "message": "Request body too large. Max: 5242880 bytes"
  }
}
```

## Metrics Endpoint

Expose Prometheus-format metrics for monitoring.

### Enable Metrics

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import create_app

app = create_app(config={
    "enable_metrics": True
})
```

### Metrics Format

```
GET /metrics
```

Response (Prometheus exposition format):

```
# HELP praisonai_http_requests_total Total HTTP requests
# TYPE praisonai_http_requests_total counter
praisonai_http_requests_total{path="/health",method="GET",status="200"} 42

# HELP praisonai_http_request_duration_seconds HTTP request duration
# TYPE praisonai_http_request_duration_seconds histogram
praisonai_http_request_duration_seconds_sum{path="/health",method="GET"} 0.123456
praisonai_http_request_duration_seconds_count{path="/health",method="GET"} 42

# HELP praisonai_http_errors_total Total HTTP errors
# TYPE praisonai_http_errors_total counter
praisonai_http_errors_total{path="/v1/recipes/run",method="POST",error_type="client_error"} 5
```

### Using with Prometheus

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# prometheus.yml
scrape_configs:
  - job_name: 'praisonai-recipe'
    static_configs:
      - targets: ['localhost:8765']
    metrics_path: '/metrics'
```

## Admin Reload Endpoint

Hot-reload recipes without restarting the server.

### Enable Admin Endpoints

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import create_app

app = create_app(config={
    "enable_admin": True,
    "auth": "api-key",
    "api_key": "admin-secret-key"
})
```

### Reload Recipes

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X POST http://localhost:8765/admin/reload \
  -H "X-API-Key: admin-secret-key"
```

Response:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "status": "reloaded",
  "timestamp": "2024-01-15T10:30:00Z"
}
```

### Programmatic Reload

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.core import reload_registry

# Clear cached recipes and reload
reload_registry()
```

## Workers (Multi-Process)

Scale with multiple worker processes.

### Configuration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import serve

# Start with 4 workers
serve(
    host="0.0.0.0",
    port=8765,
    workers=4,
    config={"auth": "api-key", "api_key": "secret"}
)
```

### Notes

* Workers > 1 automatically disables hot reload
* Each worker has its own rate limiter state (use Redis for distributed limiting)
* Recommended: 2 \* CPU cores + 1

## OpenTelemetry Tracing

Distributed tracing with OpenTelemetry.

### Configuration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import serve

serve(
    host="127.0.0.1",
    port=8765,
    config={
        "trace_exporter": "otlp",  # otlp, jaeger, zipkin
        "otlp_endpoint": "http://localhost:4317",
        "service_name": "praisonai-recipe"
    }
)
```

### Supported Exporters

| Exporter | Config Key                   | Default Endpoint                     |
| -------- | ---------------------------- | ------------------------------------ |
| OTLP     | `otlp_endpoint`              | `http://localhost:4317`              |
| Jaeger   | `jaeger_host`, `jaeger_port` | `localhost:6831`                     |
| Zipkin   | `zipkin_endpoint`            | `http://localhost:9411/api/v2/spans` |

### Install Dependencies

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# OTLP
pip install opentelemetry-sdk opentelemetry-exporter-otlp

# Jaeger
pip install opentelemetry-sdk opentelemetry-exporter-jaeger

# Zipkin
pip install opentelemetry-sdk opentelemetry-exporter-zipkin
```

### Lazy Import

OpenTelemetry dependencies are lazily imported. If not installed, a warning is logged but the server continues to work.

## OpenAPI Specification

Get the OpenAPI spec for your server.

### Endpoint

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl http://localhost:8765/openapi.json
```

### Response

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "openapi": "3.0.3",
  "info": {
    "title": "PraisonAI Recipe Runner API",
    "version": "2.7.1"
  },
  "paths": {
    "/health": {...},
    "/v1/recipes": {...},
    "/v1/recipes/run": {...},
    "/metrics": {...},
    "/admin/reload": {...}
  }
}
```

## Template Search Paths

Configure where recipes are discovered.

### Search Order

1. `PRAISONAI_RECIPE_PATH` environment variable
2. `./recipes` in current directory
3. `~/.praisonai/recipes` in home directory
4. Agent-Recipes package (if installed)
5. Built-in templates

### Get Search Paths

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.core import get_template_search_paths

paths = get_template_search_paths()
for path in paths:
    print(f"Searching: {path}")
```

### Custom Path

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export PRAISONAI_RECIPE_PATH="/custom/recipes:/another/path"
praisonai serve recipe
```

## Complete Example

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import serve

# Production-ready configuration
serve(
    host="0.0.0.0",
    port=8765,
    workers=4,
    config={
        # Authentication
        "auth": "api-key",
        "api_key": "production-secret-key",
        
        # Rate limiting
        "rate_limit": 100,
        
        # Request size
        "max_request_size": 10 * 1024 * 1024,  # 10MB
        
        # Monitoring
        "enable_metrics": True,
        "enable_admin": True,
        
        # Tracing
        "trace_exporter": "otlp",
        "otlp_endpoint": "http://otel-collector:4317",
        "service_name": "praisonai-recipe-prod",
        
        # CORS
        "cors_origins": "https://app.example.com"
    }
)
```

## Configuration Reference

| Key                       | Type | Default                                          | Description                                   |
| ------------------------- | ---- | ------------------------------------------------ | --------------------------------------------- |
| `rate_limit`              | int  | 0 (disabled)                                     | Requests per minute per client                |
| `rate_limit_exempt_paths` | list | `["/health", "/metrics"]`                        | Paths exempt from rate limiting               |
| `max_request_size`        | int  | 10485760 (10MB)                                  | Maximum request body size                     |
| `enable_metrics`          | bool | false                                            | Enable /metrics endpoint                      |
| `enable_admin`            | bool | false                                            | Enable /admin/\* endpoints                    |
| `trace_exporter`          | str  | "none"                                           | Tracing exporter (none, otlp, jaeger, zipkin) |
| `otlp_endpoint`           | str  | "[http://localhost:4317](http://localhost:4317)" | OTLP collector endpoint                       |
| `service_name`            | str  | "praisonai-recipe"                               | Service name for tracing                      |

## Next Steps

* See [CLI Usage](/docs/cli/recipe-serve-advanced) for command-line options
* Review [Recipe Serve Basics](/docs/features/recipe-serve-code)
* Explore [Endpoints](/docs/features/endpoints-code)
