Source path: app/Services/Ai/AiManager.php
Extracted from supplied specification. Review before copying into application.

<?php
namespace App\Services\Ai;

use App\Models\Organization;

class AiManager
{
    /** @return array<int,array{0:object,1:array,2:string}> [driver, config, name] in priority order */
    public function chain(string $kind, ?Organization $org): array
    {
        $drivers = config("ai.drivers.$kind");
        $out = []; $seen = [];

        if ($org) {
            $rows = $org->aiProviders()->where('kind', $kind)->where('is_enabled', 1)->orderBy('priority')->get();
            foreach ($rows as $row) {
                if (!isset($drivers[$row->provider])) continue;
                $cfg = array_merge(
                    config("ai.providers.{$row->provider}", []),
                    array_filter(['model' => $row->model]),
                    $row->credentials ?? []
                );
                $cfg['name'] = $row->provider;
                $d = app($drivers[$row->provider]);
                if ($d->configured($cfg)) { $out[] = [$d, $cfg, $row->provider]; $seen[$row->provider] = true; }
            }
        }

        // Keyless/free providers may be used as a fallback only if they explicitly
        // declare keyless support. Never fall back to another organization's key.
        foreach (config("ai.default_chain.$kind") as $name) {
            if (isset($seen[$name]) || !isset($drivers[$name])) continue;
            $cfg = config("ai.providers.$name", []) + ['name' => $name];
            if (empty($cfg['always'])) continue;
            $d = app($drivers[$name]);
            if ($d->configured($cfg)) $out[] = [$d, $cfg, $name];
        }

        return $out;
    }
}
Image drivers
