Skip to main content

Events

Instructor dispatches events throughout the extraction lifecycle. These events are bridged to Laravel’s event system, allowing you to listen and respond using standard Laravel patterns.

Event Bridge Configuration

Configure event bridging in config/instructor.php:
'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' => [
        // Only bridge specific events
        \Cognesy\Instructor\Events\ExtractionComplete::class,
        \Cognesy\Instructor\Events\ExtractionFailed::class,
    ],
],
// @doctest id="3c33"

Available Events

Extraction Events

EventDescription
ExtractionStartedExtraction process has begun
ExtractionCompleteExtraction completed successfully
ExtractionFailedExtraction failed with an error
ValidationFailedResponse failed validation
RetryAttemptedA retry attempt is being made

Inference Events

EventDescription
InferenceRequestedLLM API call initiated
InferenceCompleteLLM API call completed
InferenceFailedLLM API call failed

Streaming Events

EventDescription
StreamStartedStreaming response started
StreamChunkReceivedReceived a chunk of streaming data
StreamCompleteStreaming completed

Listening to Events

Using Event Listeners

Create a listener:
// app/Listeners/LogExtractionComplete.php
namespace App\Listeners;

use Cognesy\Instructor\Events\ExtractionComplete;
use Illuminate\Support\Facades\Log;

class LogExtractionComplete
{
    public function handle(ExtractionComplete $event): void
    {
        Log::info('Extraction completed', [
            'response_model' => $event->responseModel,
            'duration_ms' => $event->durationMs,
        ]);
    }
}
// @doctest id="4f8c"
Register in EventServiceProvider:
// app/Providers/EventServiceProvider.php
namespace App\Providers;

use App\Listeners\LogExtractionComplete;
use Cognesy\Instructor\Events\ExtractionComplete;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        ExtractionComplete::class => [
            LogExtractionComplete::class,
        ],
    ];
}
// @doctest id="3d51"

Using Closures

Register listeners in a service provider:
// app/Providers/AppServiceProvider.php
use Cognesy\Instructor\Events\ExtractionComplete;
use Cognesy\Instructor\Events\ExtractionFailed;
use Illuminate\Support\Facades\Event;

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

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

Using Event Subscribers

// app/Listeners/InstructorEventSubscriber.php
namespace App\Listeners;

use Cognesy\Instructor\Events\ExtractionComplete;
use Cognesy\Instructor\Events\ExtractionFailed;
use Cognesy\Instructor\Events\ExtractionStarted;
use Illuminate\Events\Dispatcher;

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

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

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

    public function subscribe(Dispatcher $events): array
    {
        return [
            ExtractionStarted::class => 'handleStart',
            ExtractionComplete::class => 'handleComplete',
            ExtractionFailed::class => 'handleFailed',
        ];
    }
}

// Register in EventServiceProvider
protected $subscribe = [
    InstructorEventSubscriber::class,
];
// @doctest id="31ef"

Common Use Cases

Logging and Monitoring

use Cognesy\Instructor\Events\ExtractionComplete;
use Cognesy\Instructor\Events\ExtractionFailed;
use Illuminate\Support\Facades\Log;

Event::listen(ExtractionComplete::class, function ($event) {
    Log::channel('llm')->info('Extraction successful', [
        'model' => $event->model,
        'tokens_used' => $event->tokensUsed,
        'duration_ms' => $event->durationMs,
    ]);
});

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

Metrics and Analytics

use Cognesy\Instructor\Events\ExtractionComplete;
use App\Services\MetricsService;

Event::listen(ExtractionComplete::class, function ($event) {
    app(MetricsService::class)->recordExtraction([
        'model' => $event->model,
        'tokens' => $event->tokensUsed,
        'latency' => $event->durationMs,
        'success' => true,
    ]);
});
// @doctest id="468c"

Alerting on Failures

use Cognesy\Instructor\Events\ExtractionFailed;
use Illuminate\Support\Facades\Notification;
use App\Notifications\ExtractionFailedNotification;

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

Caching Responses

use Cognesy\Instructor\Events\ExtractionComplete;
use Illuminate\Support\Facades\Cache;

Event::listen(ExtractionComplete::class, function ($event) {
    $cacheKey = 'extraction:' . md5($event->inputHash);

    Cache::put($cacheKey, $event->result, now()->addHours(24));
});
// @doctest id="bcc8"

Queued Event Listeners

For heavy processing, use queued listeners:
// app/Listeners/ProcessExtractionAnalytics.php
namespace App\Listeners;

use Cognesy\Instructor\Events\ExtractionComplete;
use Illuminate\Contracts\Queue\ShouldQueue;

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

    public function handle(ExtractionComplete $event): void
    {
        // Heavy analytics processing
    }
}
// @doctest id="50ef"

Wiretap (Direct Event Handling)

For direct access to all events without Laravel’s dispatcher:
use Cognesy\Instructor\Laravel\Facades\StructuredOutput;

$person = StructuredOutput::with(
    messages: 'Extract person data...',
    responseModel: PersonData::class,
)
->wiretap(function ($event) {
    // Called for every event
    logger()->debug('Event: ' . get_class($event));
})
->get();
// @doctest id="7291"

Disabling Event Bridge

To disable event bridging (e.g., for performance):
// config/instructor.php
'events' => [
    'dispatch_to_laravel' => false,
],
// @doctest id="92b2"
Or via environment variable:
INSTRUCTOR_DISPATCH_EVENTS=false
// @doctest id="af39"

Testing Events

Assert events were dispatched:
use Cognesy\Instructor\Events\ExtractionComplete;
use Illuminate\Support\Facades\Event;

public function test_dispatches_extraction_event(): void
{
    Event::fake([ExtractionComplete::class]);

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

    Event::assertDispatched(ExtractionComplete::class);
}
// @doctest id="f0cc"
Assert event properties:
Event::assertDispatched(ExtractionComplete::class, function ($event) {
    return $event->responseModel === PersonData::class
        && $event->tokensUsed > 0;
});
// @doctest id="d19d"