# Custom Model Data Fields

Add extra fields to `model_data_logs` for custom tracking and monitoring needs.

## Overview

The `extra_model_data` and `extra_model_data_fn` parameters allow you to include additional fields in the encrypted `model_data_logs` sent to the RAIT API. These fields are merged with standard fields like `prompt_id`, `ethical_dimensions`, and telemetry data.

## Static Extra Data

Use `extra_model_data` to pass a dictionary of static values:

```python
from rait_connector import RAITClient

client = RAITClient()

result = client.evaluate(
    prompt_id="123",
    prompt_url="https://example.com/123",
    timestamp="2025-12-11T10:00:00Z",
    model_name="gpt-4",
    model_version="1.0",
    query="What is AI?",
    response="AI is artificial intelligence...",
    environment="production",
    purpose="monitoring",
    extra_model_data={
        "user_id": "user_123",
        "session_id": "session_abc",
        "custom_metric": 0.95,
        "api_version": "v2.1",
        "region": "us-east-1"
    }
)
```

## Dynamic Extra Data with Functions

Use `extra_model_data_fn` to compute values dynamically at evaluation time:

```python
import psutil
from datetime import datetime
from rait_connector import RAITClient

client = RAITClient()

def get_runtime_metrics():
    """Compute system metrics at evaluation time."""
    return {
        "evaluation_timestamp": datetime.now().isoformat(),
        "cpu_percent": psutil.cpu_percent(),
        "memory_available_mb": psutil.virtual_memory().available / (1024 * 1024),
        "disk_usage_percent": psutil.disk_usage('/').percent
    }

result = client.evaluate(
    prompt_id="123",
    prompt_url="https://example.com/123",
    timestamp="2025-12-11T10:00:00Z",
    model_name="gpt-4",
    model_version="1.0",
    query="What is AI?",
    response="AI is artificial intelligence...",
    environment="production",
    purpose="monitoring",
    extra_model_data_fn=get_runtime_metrics
)
```

## Combining Both Approaches

You can use both parameters together - static and dynamic data will be merged:

```python
from datetime import datetime
from rait_connector import RAITClient

client = RAITClient()

def get_timestamp():
    return {"evaluation_timestamp": datetime.now().isoformat()}

result = client.evaluate(
    prompt_id="123",
    # ... standard parameters ...
    extra_model_data={"user_id": "user_123", "app_version": "2.1.0"},
    extra_model_data_fn=get_timestamp
)
```

## Use Cases

### User Context Tracking

```python
extra_model_data = {
    "user_id": current_user.id,
    "organization_id": current_user.org_id,
    "subscription_tier": current_user.subscription
}
```

### Performance Monitoring

```python
def get_performance_data():
    return {
        "response_time_ms": response_timer.elapsed(),
        "tokens_used": token_counter.count,
        "cache_hit": cache.was_hit()
    }

client.evaluate(..., extra_model_data_fn=get_performance_data)
```

### A/B Testing

```python
extra_model_data = {
    "experiment_id": "exp_123",
    "variant": "variant_b",
    "feature_flags": ["new_ui", "enhanced_search"]
}
```

### Custom Business Metrics

```python
extra_model_data = {
    "customer_segment": "enterprise",
    "request_priority": "high",
    "sla_tier": "gold",
    "cost_center": "engineering"
}
```

## Error Handling

If `extra_model_data_fn` raises an exception, the error is logged but evaluation continues without the dynamic data:

```python
def potentially_failing_function():
    # If this fails, evaluation still proceeds
    return {"risky_metric": compute_expensive_metric()}

client.evaluate(
    ...,
    extra_model_data_fn=potentially_failing_function
)
```

## Data Flow

1. Base `model_data_logs` is constructed with standard fields
2. `extra_model_data` dict is merged (if provided)
3. `extra_model_data_fn()` is called and result is merged (if provided)
4. Complete `model_data_logs` is encrypted
5. Encrypted data is sent to RAIT API

## Batch Evaluation

Both parameters work with `evaluate_batch()`:

```python
client.evaluate_batch(
    prompts=[prompt1, prompt2, prompt3],
    extra_model_data={"batch_id": "batch_456"},
    extra_model_data_fn=lambda: {"batch_timestamp": datetime.now().isoformat()}
)
```

All prompts in the batch will include the same extra data.

## Best Practices

1. **Keep it simple**: Only add fields you actually need
2. **Avoid PII**: Don't include sensitive personal information
3. **Use meaningful names**: Make field names descriptive and consistent
4. **Consider performance**: Dynamic functions run on every evaluation
5. **Handle errors**: Ensure functions don't raise exceptions unnecessarily
6. **Document custom fields**: Keep track of what each field means
