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

# Events

# Events

Instructor dispatches events throughout the extraction and inference lifecycle. These events are automatically bridged to Laravel's event system by the `LaravelEventDispatcher`, allowing you to listen and respond using standard Laravel patterns -- listeners, subscribers, closures, and queued handlers.

The bridge is implemented by `Cognesy\Instructor\Laravel\Events\LaravelEventDispatcher`, which lives in the `packages/laravel` package. It wraps Laravel's native `Illuminate\Contracts\Events\Dispatcher` and forwards Instructor events to it based on your configuration.

## Event Bridge Configuration

Configure event bridging in `config/instructor.php`:

```php theme={null}
return [
    'events' => [
        // Enable bridging to Laravel's event dispatcher
        'dispatch_to_laravel' => env('INSTRUCTOR_DISPATCH_EVENTS', true),
        // Specify which events to bridge (empty = all events)
        'bridge_events' => [
            \Cognesy\Instructor\Events\Extraction\ExtractionCompleted::class,
            \Cognesy\Instructor\Events\Extraction\ExtractionFailed::class,
        ],
    ],
];
// @doctest id="a7ec"
```

When `bridge_events` is empty (the default), every Instructor event is forwarded to Laravel's dispatcher. To reduce overhead in production, list only the event classes your listeners actually need.

The bridge uses `instanceof` matching, so listing a parent event class also bridges its subclasses.

## Available Events

All events extend `Cognesy\Instructor\Events\StructuredOutputEvent` (which extends `Cognesy\Events\Event`). Events carry data in the `$data` property (an array or mixed value) rather than typed properties.

### Extraction Events

Namespace: `Cognesy\Instructor\Events\Extraction`

| Event                         | Description                              |
| ----------------------------- | ---------------------------------------- |
| `ExtractionStarted`           | Extraction pipeline has begun processing |
| `ExtractionCompleted`         | Extraction completed successfully        |
| `ExtractionFailed`            | All extraction strategies failed         |
| `ExtractionStrategyAttempted` | An extraction strategy was attempted     |
| `ExtractionStrategyFailed`    | An extraction strategy failed            |
| `ExtractionStrategySucceeded` | An extraction strategy succeeded         |

### Response Events

Namespace: `Cognesy\Instructor\Events\Response`

| Event                           | Description                              |
| ------------------------------- | ---------------------------------------- |
| `ResponseValidationFailed`      | Response failed validation               |
| `ResponseValidated`             | Response passed validation               |
| `ResponseDeserialized`          | Response was deserialized into an object |
| `ResponseDeserializationFailed` | Response deserialization failed          |
| `ResponseTransformed`           | Response was transformed                 |
| `ResponseTransformationFailed`  | Response transformation failed           |
| `ResponseGenerationFailed`      | Response generation failed               |

### Request Events

Namespace: `Cognesy\Instructor\Events\Request`

| Event                                  | Description                                       |
| -------------------------------------- | ------------------------------------------------- |
| `NewValidationRecoveryAttempt`         | A validation recovery retry attempt is being made |
| `StructuredOutputRecoveryLimitReached` | Maximum retries exhausted                         |
| `ResponseModelRequested`               | Response model was requested                      |
| `ResponseModelBuilt`                   | Response model schema was built                   |

### Streaming Events

Namespace: `Cognesy\Instructor\Events\PartialsGenerator`

| Event                       | Description                             |
| --------------------------- | --------------------------------------- |
| `StreamedResponseReceived`  | Streaming response started              |
| `ChunkReceived`             | Received a chunk of streaming data      |
| `StreamedResponseFinished`  | Streaming completed                     |
| `PartialResponseGenerated`  | A partial response object was generated |
| `StreamedToolCallStarted`   | A streamed tool call started            |
| `StreamedToolCallUpdated`   | A streamed tool call was updated        |
| `StreamedToolCallCompleted` | A streamed tool call completed          |

## Listening to Events

### Using Event Listeners

Create a dedicated listener class and register it with Laravel's event system.

```php theme={null}
// app/Listeners/LogExtractionCompleted.php
namespace App\Listeners;

use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Illuminate\Support\Facades\Log;

class LogExtractionCompleted
{
    public function handle(ExtractionCompleted $event): void
    {
        Log::info('Extraction completed', [
            'event' => $event->name(),
            'data' => $event->data,
        ]);
    }
}
// @doctest id="5370"
```

Register in `EventServiceProvider`:

```php theme={null}
// app/Providers/EventServiceProvider.php
namespace App\Providers;

use App\Listeners\LogExtractionCompleted;
use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        ExtractionCompleted::class => [
            LogExtractionCompleted::class,
        ],
    ];
}
// @doctest id="fdc6"
```

### Using Closures

For lightweight listeners, register closures directly in a service provider's `boot` method.

```php theme={null}
// app/Providers/AppServiceProvider.php
namespace App\Providers;

use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Cognesy\Instructor\Events\Extraction\ExtractionFailed;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Event::listen(ExtractionCompleted::class, function ($event) {
            // Handle successful extraction
        });

        Event::listen(ExtractionFailed::class, function ($event) {
            // Handle failed extraction
        });
    }
}
// @doctest id="f43e"
```

### Using Event Subscribers

Group related event handlers into a single subscriber class. This is convenient when you need to handle multiple Instructor events together.

```php theme={null}
// app/Listeners/InstructorEventSubscriber.php
namespace App\Listeners;

use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Cognesy\Instructor\Events\Extraction\ExtractionFailed;
use Cognesy\Instructor\Events\Extraction\ExtractionStarted;
use Illuminate\Events\Dispatcher;

class InstructorEventSubscriber
{
    public function handleStart(ExtractionStarted $event): void
    {
        // Log start
    }

    public function handleComplete(ExtractionCompleted $event): void
    {
        // Log completion
    }

    public function handleFailed(ExtractionFailed $event): void
    {
        // Alert on failure
    }

    public function subscribe(Dispatcher $events): array
    {
        return [
            ExtractionStarted::class => 'handleStart',
            ExtractionCompleted::class => 'handleComplete',
            ExtractionFailed::class => 'handleFailed',
        ];
    }
}
// @doctest id="8904"
```

Register it in `EventServiceProvider`:

```php theme={null}
// app/Providers/EventServiceProvider.php
namespace App\Providers;

use App\Listeners\InstructorEventSubscriber;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $subscribe = [
        InstructorEventSubscriber::class,
    ];
}
// @doctest id="8fc1"
```

## Common Use Cases

### Logging and Monitoring

All events carry data in the `$data` property (typically an array). Use the `name()` method to get the event class short name.

```php theme={null}
use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Cognesy\Instructor\Events\Extraction\ExtractionFailed;
use Illuminate\Support\Facades\Log;

Event::listen(ExtractionCompleted::class, function ($event) {
    Log::channel('llm')->info('Extraction successful', [
        'event' => $event->name(),
        'data' => $event->data,
    ]);
});

Event::listen(ExtractionFailed::class, function ($event) {
    Log::channel('llm')->error('Extraction failed', [
        'event' => $event->name(),
        'data' => $event->data,
    ]);
});
// @doctest id="2815"
```

### Metrics and Analytics

```php theme={null}
use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use App\Services\MetricsService;

Event::listen(ExtractionCompleted::class, function ($event) {
    app(MetricsService::class)->recordExtraction([
        'event' => $event->name(),
        'data' => $event->data,
    ]);
});
// @doctest id="5918"
```

### Alerting on Failures

```php theme={null}
use Cognesy\Instructor\Events\Extraction\ExtractionFailed;
use Illuminate\Support\Facades\Notification;
use App\Notifications\ExtractionFailedNotification;

Event::listen(ExtractionFailed::class, function ($event) {
    Notification::route('slack', config('services.slack.webhook'))
        ->notify(new ExtractionFailedNotification($event));
});
// @doctest id="9f87"
```

### Queued Event Listeners

For CPU-intensive or I/O-heavy processing, implement `ShouldQueue` to push the work onto a queue instead of running it inline.

```php theme={null}
// app/Listeners/ProcessExtractionAnalytics.php
namespace App\Listeners;

use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessExtractionAnalytics implements ShouldQueue
{
    public $queue = 'analytics';

    public function handle(ExtractionCompleted $event): void
    {
        // Heavy analytics processing runs on the queue
    }
}
// @doctest id="25f1"
```

## Wiretap (Direct Event Handling)

The `wiretap` method provides direct access to the raw event stream without going through Laravel's dispatcher. This is useful for low-level debugging or when you need to observe every internal event.

```php theme={null}
use Cognesy\Instructor\Laravel\Facades\StructuredOutput;
use Cognesy\Instructor\StructuredOutputRuntime;
use Cognesy\Polyglot\Inference\LLMProvider;

$runtime = StructuredOutputRuntime::fromProvider(LLMProvider::new())
    ->wiretap(function ($event) {
        // Called for every event in the pipeline
        logger()->debug('Event: ' . get_class($event));
    });

$person = StructuredOutput::withRuntime($runtime)->with(
    messages: 'Extract person data...',
    responseModel: PersonData::class,
)
->get();
// @doctest id="61cc"
```

The `LaravelEventDispatcher` itself also supports `wiretap` for registering global listeners that receive every event, regardless of class. These listeners run at the lowest priority after all class-specific and bridged listeners have executed.

## Disabling Event Bridge

To disable event bridging entirely (for example, in high-throughput scenarios where the overhead is unacceptable):

```php theme={null}
return [
    'events' => [
        'dispatch_to_laravel' => false,
    ],
];
// @doctest id="4325"
```

Or via environment variable:

```env theme={null}
INSTRUCTOR_DISPATCH_EVENTS=false
// @doctest id="4644"
```

Disabling the bridge only stops events from being forwarded to Laravel's dispatcher. Internal Instructor event listeners and wiretaps continue to work normally.

## Testing Events

Use Laravel's `Event::fake()` to assert that specific events were dispatched during a test.

```php theme={null}
use Cognesy\Instructor\Events\Extraction\ExtractionCompleted;
use Cognesy\Instructor\Laravel\Facades\StructuredOutput;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;

final class EventBridgeTest extends TestCase
{
    public function test_dispatches_extraction_event(): void
    {
        Event::fake([ExtractionCompleted::class]);

        StructuredOutput::with(
            messages: 'John is 30',
            responseModel: PersonData::class,
        )->get();

        Event::assertDispatched(ExtractionCompleted::class);
    }
}
// @doctest id="bef6"
```

Assert event data with a closure:

```php theme={null}
Event::assertDispatched(ExtractionCompleted::class, function ($event) {
    return !empty($event->data);
});
// @doctest id="e013"
```
