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

# Mocker

> Mock AI provider responses for testing, development, and simulation purposes.

## Quick Start

### Minimal Configuration

The simplest way to use the Mocker plugin is with no configuration - it will create a default catch-all rule:

```go theme={null}
package main

import (
    "context"
    bifrost "github.com/maximhq/bifrost/core"
    "github.com/maximhq/bifrost/core/schemas"
    mocker "github.com/maximhq/bifrost/plugins/mocker"
)

func main() {
    // Create plugin with minimal config
    plugin, err := mocker.NewMockerPlugin(mocker.MockerConfig{
        Enabled: true, // Default rule will be created automatically
    })
    if err != nil {
        panic(err)
    }

    // Initialize Bifrost with the plugin
    client, initErr := bifrost.Init(context.Background(), schemas.BifrostConfig{
        Account: &yourAccount,
        LLMPlugins: []schemas.LLMPlugin{plugin},
    })
    if err != nil {
        panic(err)
    }
    defer client.Shutdown()

    // All chat and responses requests will now return: "This is a mock response from the Mocker plugin"
    
    // Chat completion request
    chatResponse, _ := client.ChatCompletionRequest(schemas.NewBifrostContext(context.Background(), schemas.NoDeadline), &schemas.BifrostChatRequest{
        Provider: schemas.OpenAI,
        Model:    "gpt-4",
        Input: []schemas.ChatMessage{
            {
                Role: schemas.ChatMessageRoleUser,
                Content: schemas.ChatMessageContent{
                    ContentStr: bifrost.Ptr("Hello!"),
                },
            },
        },
    })
    
    // Responses request
    responsesResponse, _ := client.ResponsesRequest(schemas.NewBifrostContext(context.Background(), schemas.NoDeadline), &schemas.BifrostResponsesRequest{
        Provider: schemas.OpenAI,
        Model:    "gpt-4o",
        Input: []schemas.ResponsesMessage{
            {
                Role: bifrost.Ptr(schemas.ResponsesInputMessageRoleUser),
                Content: &schemas.ResponsesMessageContent{
                    ContentStr: bifrost.Ptr("Hello!"),
                },
            },
        },
    })
}
```

### Custom Response

```go theme={null}
plugin, err := mocker.NewMockerPlugin(mocker.MockerConfig{
    Enabled: true,
    Rules: []mocker.MockRule{
        {
            Name:        "openai-mock",
            Enabled:     true,
            Probability: 1.0, // Always trigger
            Conditions: mocker.Conditions{
                Providers: []string{"openai"},
            },
            Responses: []mocker.Response{
                {
                    Type: mocker.ResponseTypeSuccess,
                    Content: &mocker.SuccessResponse{
                        Message: "Hello! This is a custom mock response for OpenAI.",
                        Usage: &mocker.Usage{
                            PromptTokens:     15,
                            CompletionTokens: 25,
                            TotalTokens:      40,
                        },
                    },
                },
            },
        },
    },
})
```

### Responses Request Example

The mocker plugin automatically handles both chat completion and responses requests with the same configuration:

```go theme={null}
// This rule will work for both ChatCompletionRequest and ResponsesRequest
{
    Name:        "universal-mock",
    Enabled:     true,
    Probability: 1.0,
    Conditions: mocker.Conditions{
        MessageRegex: stringPtr("(?i).*hello.*"),
    },
    Responses: []mocker.Response{
        {
            Type: mocker.ResponseTypeSuccess,
            Content: &mocker.SuccessResponse{
                Message: "Hello! I'm a mock response that works for both request types.",
            },
        },
    },
}
```

## Installation

Add the plugin to your project:

```bash theme={null}
go get github.com/maximhq/bifrost/plugins/mocker
```

Import in your code:

```go theme={null}
import mocker "github.com/maximhq/bifrost/plugins/mocker"
```

## Basic Usage

### Creating the Plugin

```go theme={null}
config := mocker.MockerConfig{
    Enabled: true,
    DefaultBehavior: mocker.DefaultBehaviorPassthrough, // "passthrough", "success", "error"
    Rules: []mocker.MockRule{
        // Your rules here
    },
}

plugin, err := mocker.NewMockerPlugin(config)
if err != nil {
    log.Fatal(err)
}
```

### Adding to Bifrost

```go theme={null}
client, initErr := bifrost.Init(context.Background(), schemas.BifrostConfig{
    Account: &yourAccount,
    LLMPlugins: []schemas.LLMPlugin{plugin},
    Logger: bifrost.NewDefaultLogger(schemas.LogLevelInfo),
})
```

### Disabling the Plugin

```go theme={null}
config := mocker.MockerConfig{
    Enabled: false, // All requests pass through to real providers
}
```

## Supported Request Types

The Mocker plugin supports the following Bifrost request types:

* **Chat Completion Requests** (`ChatCompletionRequest`) - Standard chat-based interactions
* **Responses Requests** (`ResponsesRequest`) - OpenAI-compatible responses API format
* **Skip Context Key** - Use `"skip-mocker"` context key to bypass mocking per request

### Skip Mocker for Specific Requests

You can skip the mocker plugin for specific requests by adding a context key:

```go theme={null}
import "github.com/maximhq/bifrost/core/schemas"

// Create context that skips mocker
ctx := context.WithValue(context.Background(), 
    schemas.BifrostContextKey("skip-mocker"), true)

// This request will bypass the mocker and go to the real provider
response, err := client.ChatCompletionRequest(schemas.NewBifrostContext(ctx, schemas.NoDeadline), request)
```

## Key Features

### Template Variables

Create dynamic responses using templates:

```go theme={null}
Response{
    Type: mocker.ResponseTypeSuccess,
    Content: &mocker.SuccessResponse{
        MessageTemplate: stringPtr("Hello from {{provider}} using model {{model}}!"),
    },
}
```

**Available Variables:**

* `{{provider}}` - Provider name (e.g., "openai", "anthropic")
* `{{model}}` - Model name (e.g., "gpt-4", "claude-3")
* `{{faker.*}}` - Fake data generation (see Configuration Reference)

### Weighted Response Selection

Configure multiple responses with different probabilities:

```go theme={null}
Responses: []mocker.Response{
    {
        Type:   mocker.ResponseTypeSuccess,
        Weight: 0.8, // 80% chance
        Content: &mocker.SuccessResponse{
            Message: "Success response",
        },
    },
    {
        Type:   mocker.ResponseTypeError,
        Weight: 0.2, // 20% chance
        Error: &mocker.ErrorResponse{
            Message: "Rate limit exceeded",
            Type:    stringPtr("rate_limit"),
            Code:    stringPtr("429"),
        },
    },
}
```

### Latency Simulation

Add realistic delays to responses:

```go theme={null}
// Fixed latency
Latency: &mocker.Latency{
    Type: mocker.LatencyTypeFixed,
    Min:  250 * time.Millisecond,
}

// Variable latency
Latency: &mocker.Latency{
    Type: mocker.LatencyTypeUniform,
    Min:  100 * time.Millisecond,
    Max:  500 * time.Millisecond,
}
```

### Advanced Matching

#### Regex Message Matching

```go theme={null}
Conditions: mocker.Conditions{
    MessageRegex: stringPtr(`(?i).*support.*|.*help.*`),
}
```

#### Request Size Filtering

```go theme={null}
Conditions: mocker.Conditions{
    RequestSize: &mocker.SizeRange{
        Min: 100,  // bytes
        Max: 1000, // bytes
    },
}
```

### Faker Data Generation

Create realistic test data using faker variables:

```go theme={null}
{
    Name: "user-profile-example",
    Responses: []mocker.Response{
        {
            Type: mocker.ResponseTypeSuccess,
            Content: &mocker.SuccessResponse{
                MessageTemplate: stringPtr(`User Profile:
- Name: {{faker.name}}
- Email: {{faker.email}}
- Company: {{faker.company}}
- Address: {{faker.address}}, {{faker.city}}
- Phone: {{faker.phone}}
- User ID: {{faker.uuid}}
- Join Date: {{faker.date}}
- Premium Account: {{faker.boolean}}`),
            },
        },
    },
}
```

### Statistics and Monitoring

Get runtime statistics for monitoring:

```go theme={null}
stats := plugin.GetStatistics()
fmt.Printf("Plugin enabled: %v\n", stats.Enabled)
fmt.Printf("Total requests: %d\n", stats.TotalRequests)
fmt.Printf("Mocked requests: %d\n", stats.MockedRequests)

// Rule-specific stats
for ruleName, ruleStats := range stats.Rules {
    fmt.Printf("Rule %s: %d triggers\n", ruleName, ruleStats.Triggers)
}
```

## Configuration Reference

### MockerConfig

| Field             | Type         | Default         | Description                                                         |
| ----------------- | ------------ | --------------- | ------------------------------------------------------------------- |
| `Enabled`         | `bool`       | `false`         | Enable/disable the entire plugin                                    |
| `DefaultBehavior` | `string`     | `"passthrough"` | Action when no rules match: `"passthrough"`, `"success"`, `"error"` |
| `GlobalLatency`   | `*Latency`   | `nil`           | Global latency applied to all rules                                 |
| `Rules`           | `[]MockRule` | `[]`            | List of mock rules evaluated in priority order                      |

### MockRule

| Field         | Type         | Default | Description                                    |
| ------------- | ------------ | ------- | ---------------------------------------------- |
| `Name`        | `string`     | -       | Unique rule name for identification            |
| `Enabled`     | `bool`       | `true`  | Enable/disable this specific rule              |
| `Priority`    | `int`        | `0`     | Higher numbers = higher priority               |
| `Probability` | `float64`    | `1.0`   | Activation probability (0.0=never, 1.0=always) |
| `Conditions`  | `Conditions` | `{}`    | Matching conditions (empty = match all)        |
| `Responses`   | `[]Response` | -       | Possible responses (weighted random selection) |
| `Latency`     | `*Latency`   | `nil`   | Rule-specific latency override                 |

### Conditions

| Field          | Type         | Description                                         |
| -------------- | ------------ | --------------------------------------------------- |
| `Providers`    | `[]string`   | Match specific providers: `["openai", "anthropic"]` |
| `Models`       | `[]string`   | Match specific models: `["gpt-4", "claude-3"]`      |
| `MessageRegex` | `*string`    | Regex pattern to match message content              |
| `RequestSize`  | `*SizeRange` | Request size constraints in bytes                   |

### Response

| Field            | Type               | Description                                            |
| ---------------- | ------------------ | ------------------------------------------------------ |
| `Type`           | `string`           | Response type: `"success"` or `"error"`                |
| `Weight`         | `float64`          | Weight for random selection (default: 1.0)             |
| `Content`        | `*SuccessResponse` | Required if `Type="success"`                           |
| `Error`          | `*ErrorResponse`   | Required if `Type="error"`                             |
| `AllowFallbacks` | `*bool`            | Control fallback behavior (`nil`=allow, `false`=block) |

### SuccessResponse

| Field             | Type                     | Description                                                         |
| ----------------- | ------------------------ | ------------------------------------------------------------------- |
| `Message`         | `string`                 | Static response message                                             |
| `MessageTemplate` | `*string`                | Template with variables: `{{provider}}`, `{{model}}`, `{{faker.*}}` |
| `Model`           | `*string`                | Override model name in response                                     |
| `Usage`           | `*Usage`                 | Token usage information                                             |
| `FinishReason`    | `*string`                | Completion reason (default: `"stop"`)                               |
| `CustomFields`    | `map[string]interface{}` | Additional metadata fields                                          |

### ErrorResponse

| Field        | Type      | Description                                       |
| ------------ | --------- | ------------------------------------------------- |
| `Message`    | `string`  | Error message to return                           |
| `Type`       | `*string` | Error type (e.g., `"rate_limit"`, `"auth_error"`) |
| `Code`       | `*string` | Error code (e.g., `"429"`, `"401"`)               |
| `StatusCode` | `*int`    | HTTP status code                                  |

### Latency

| Field  | Type            | Description                                    |
| ------ | --------------- | ---------------------------------------------- |
| `Type` | `string`        | Latency type: `"fixed"` or `"uniform"`         |
| `Min`  | `time.Duration` | Minimum/exact latency (use `time.Millisecond`) |
| `Max`  | `time.Duration` | Maximum latency (required for `"uniform"`)     |

**Important**: Use Go's `time.Duration` constants:

* ✅ Correct: `100 * time.Millisecond`
* ❌ Wrong: `100` (nanoseconds, barely noticeable)

### Faker Variables

#### Personal Information

* `{{faker.name}}` - Full name
* `{{faker.first_name}}` - First name only
* `{{faker.last_name}}` - Last name only
* `{{faker.email}}` - Email address
* `{{faker.phone}}` - Phone number

#### Location

* `{{faker.address}}` - Street address
* `{{faker.city}}` - City name
* `{{faker.state}}` - State/province
* `{{faker.zip_code}}` - Postal code

#### Business

* `{{faker.company}}` - Company name
* `{{faker.job_title}}` - Job title

#### Text and Data

* `{{faker.lorem_ipsum}}` - Lorem ipsum text
* `{{faker.lorem_ipsum:10}}` - Lorem ipsum with 10 words
* `{{faker.uuid}}` - UUID v4
* `{{faker.hex_color}}` - Hex color code

#### Numbers and Dates

* `{{faker.integer}}` - Random integer (1-100)
* `{{faker.integer:10,50}}` - Random integer between 10-50
* `{{faker.float}}` - Random float (0-100, 2 decimals)
* `{{faker.float:1,10}}` - Random float between 1-10
* `{{faker.boolean}}` - Random boolean
* `{{faker.date}}` - Date (YYYY-MM-DD format)
* `{{faker.datetime}}` - Datetime (YYYY-MM-DD HH:MM:SS format)

## Best Practices

### Rule Organization

```go theme={null}
// Use priority to control rule evaluation order
rules := []mocker.MockRule{
    {Name: "specific-error", Priority: 100, Conditions: /* specific */},
    {Name: "general-success", Priority: 50, Conditions: /* general */},
    {Name: "catch-all", Priority: 0, Conditions: /* empty */},
}
```

### Development vs Production

```go theme={null}
// Development: High mock rate
config := mocker.MockerConfig{
    Enabled: true,
    Rules: []mocker.MockRule{
        {Probability: 1.0}, // Always mock
    },
}

// Production: Occasional testing
config := mocker.MockerConfig{
    Enabled: true,
    Rules: []mocker.MockRule{
        {Probability: 0.1}, // 10% mock rate
    },
}
```

### Performance Considerations

* Place specific conditions before general ones (higher priority)
* Use simple string matching over complex regex when possible
* Keep response templates reasonably sized
* Consider disabling debug logging in production

### Testing Your Configuration

```go theme={null}
func validateMockerConfig(config mocker.MockerConfig) error {
    _, err := mocker.NewMockerPlugin(config)
    return err
}

// Test before deployment
if err := validateMockerConfig(yourConfig); err != nil {
    log.Fatalf("Invalid mocker configuration: %v", err)
}
```

## Common Issues

### Plugin Not Triggering

1. Check if plugin is enabled: `Enabled: true`
2. Verify rule is enabled: `rule.Enabled: true`
3. Check probability: `Probability: 1.0` for testing
4. Verify conditions match your request

### Latency Not Working

Use `time.Duration` constants, not raw integers:

```go theme={null}
// ❌ Wrong: 100 nanoseconds (barely noticeable)
Min: 100

// ✅ Correct: 100 milliseconds
Min: 100 * time.Millisecond
```

### Regex Not Matching

Test your regex pattern and ensure proper escaping:

```go theme={null}
// Case-insensitive matching
MessageRegex: stringPtr(`(?i).*help.*`)

// Escape special characters
MessageRegex: stringPtr(`\$\d+\.\d+`) // Match $12.34
```

### Controlling Fallbacks

```go theme={null}
Response{
    Type: mocker.ResponseTypeError,
    AllowFallbacks: boolPtr(false), // Block fallbacks
    Error: &mocker.ErrorResponse{
        Message: "Authentication failed",
    },
}
```

### Skip Mocker Not Working

Ensure you're using the correct context key format:

```go theme={null}
// ✅ Correct
ctx := context.WithValue(context.Background(), 
    schemas.BifrostContextKey("skip-mocker"), true)

// ❌ Wrong
ctx := context.WithValue(context.Background(), "skip-mocker", true)
```

### Responses Request Issues

If responses requests aren't being mocked:

1. Verify the plugin supports `ResponsesRequest` (version 1.2.13+)
2. Check that your regex patterns match the message content
3. Ensure the request type is `schemas.ResponsesRequest`

### Debug Mode

Enable debug logging to troubleshoot:

```go theme={null}
client, initErr := bifrost.Init(context.Background(), schemas.BifrostConfig{
    Account: &account,
    LLMPlugins: []schemas.LLMPlugin{plugin},
    Logger:  bifrost.NewDefaultLogger(schemas.LogLevelDebug),
})
```
