isConfigured()) { $this->error('Vision vector gateway is not configured. Set VISION_VECTOR_GATEWAY_URL and VISION_VECTOR_GATEWAY_API_KEY.'); return self::FAILURE; } $artworkId = max(1, (int) $this->argument('artwork_id')); $limit = max(1, min((int) $this->option('limit'), 100)); $artwork = Artwork::query() ->with(['categories' => fn ($categories) => $categories->with('contentType')->orderBy('sort_order')->orderBy('name')]) ->find($artworkId); if (! $artwork) { $this->error("Artwork {$artworkId} was not found."); return self::FAILURE; } $url = $imageUrl->fromArtwork($artwork); if ($url === null) { $this->error("Artwork {$artworkId} does not have a usable CDN image URL."); return self::FAILURE; } try { $matches = $client->searchByUrl($url, $limit + 1); } catch (\Throwable $e) { $this->error('Vector search failed: ' . $e->getMessage()); return self::FAILURE; } $ids = collect($matches) ->map(fn (array $match): int => (int) $match['id']) ->filter(fn (int $id): bool => $id > 0 && $id !== $artworkId) ->unique() ->take($limit) ->values() ->all(); if ($ids === []) { $this->warn('No similar artworks were returned by the vector gateway.'); return self::SUCCESS; } $artworks = Artwork::query() ->with(['categories' => fn ($categories) => $categories->with('contentType')->orderBy('sort_order')->orderBy('name')]) ->whereIn('id', $ids) ->public() ->published() ->get() ->keyBy('id'); $rows = []; foreach ($matches as $match) { $matchId = (int) ($match['id'] ?? 0); if ($matchId <= 0 || $matchId === $artworkId) { continue; } /** @var Artwork|null $matchedArtwork */ $matchedArtwork = $artworks->get($matchId); if (! $matchedArtwork) { continue; } $category = $this->primaryCategory($matchedArtwork); $rows[] = [ 'id' => $matchId, 'score' => number_format((float) ($match['score'] ?? 0.0), 4, '.', ''), 'title' => (string) $matchedArtwork->title, 'content_type' => (string) ($category?->contentType?->name ?? ''), 'category' => (string) ($category?->name ?? ''), ]; if (count($rows) >= $limit) { break; } } if ($rows === []) { $this->warn('The vector gateway returned matches, but none resolved to public published artworks.'); return self::SUCCESS; } $this->table(['ID', 'Score', 'Title', 'Content Type', 'Category'], $rows); return self::SUCCESS; } private function primaryCategory(Artwork $artwork): ?Category { /** @var Category|null $category */ $category = $artwork->categories->sortBy('sort_order')->first(); return $category; } }