where('organization_id', $organizationId) ->where('enabled', true) ->orderByDesc('is_default') ->orderBy('created_at') ->orderBy('id') ->first(); if ($connection) { return $this->fromConnection($connection); } $row = DB::table('organization_profiles')->where('organization_id', $organizationId)->first(); $settings = json_decode($row?->settings ?: '{}', true); return ($settings['ai']['provider'] ?? 'local') === 'openai' ? $this->openAi : $this->local; } public function byId(string $id, string $organizationId): AiProvider { if (str_starts_with($id, 'connection:')) { $connection = DB::table('ai_provider_connections') ->where('organization_id', $organizationId) ->where('id', substr($id, 11)) ->where('enabled', true) ->first(); if ($connection) { return $this->fromConnection($connection); } throw new RuntimeException('The selected AI provider connection is unavailable.'); } return match ($id) { 'openai' => $this->openAi, 'local', 'local-structuring-v1' => $this->local, default => throw new RuntimeException('The selected AI provider is unknown.'), }; } public function fallbackForOrganization(string $organizationId, string $failedProviderId): ?AiProvider { $settings = $this->settings($organizationId); $enabled = (bool) ($settings['reliability']['fallbackEnabled'] ?? true); $fallback = $settings['fallbackProvider'] ?? 'local'; $fallbackConnectionId = $settings['fallbackConnectionId'] ?? null; if ($enabled && $fallbackConnectionId && ! str_contains($failedProviderId, $fallbackConnectionId)) { $connection = DB::table('ai_provider_connections')->where('organization_id', $organizationId)->where('id', $fallbackConnectionId)->where('enabled', true)->first(); if ($connection) { return $this->fromConnection($connection); } } return $enabled && $failedProviderId !== 'local' && $fallback === 'local' ? $this->local : null; } /** @return array */ public function status(string $organizationId): array { $provider = $this->forOrganization($organizationId); if ($provider instanceof OpenAiCompatibleProvider) { return ['id' => $provider->id(), 'external' => $provider->external(), 'configured' => $provider->configured(), ...$provider->health()]; } return ['id' => $provider->id(), 'external' => false, 'configured' => true, 'connected' => true, 'latencyMs' => 0, 'message' => 'پردازش محلی آماده است.']; } /** @return array */ private function settings(string $organizationId): array { $row = DB::table('organization_profiles')->where('organization_id', $organizationId)->first(); $settings = json_decode($row?->settings ?: '{}', true); return $settings['ai'] ?? []; } private function fromConnection(object $row): OpenAiCompatibleProvider { return new OpenAiCompatibleProvider($this->endpointPolicy, [ 'id' => $row->id, 'provider' => $row->provider, 'mode' => $row->mode, 'base_url' => $row->base_url, 'api_key' => filled($row->encrypted_api_key) ? Crypt::decryptString($row->encrypted_api_key) : '', 'default_model' => $row->default_model, 'timeout_seconds' => $row->timeout_seconds, ]); } }