public()->published()->find($id); if ($artwork === null) { return response()->json(['error' => 'Artwork not found'], 404); } if (! $this->vectors->isConfigured()) { return response()->json([ 'data' => [], 'reason' => 'vector_gateway_not_configured', ], 503); } $limit = max(1, min(24, (int) $request->query('limit', 12))); try { $items = $this->vectors->similarToArtwork($artwork, $limit); } catch (Throwable $e) { $this->logSimilarityFailure($artwork->id, $e); return response()->json([ 'data' => [], 'reason' => 'vector_gateway_error', ], 502); } return response()->json([ 'data' => $items, 'meta' => [ 'source' => 'vector_gateway', 'artwork_id' => $artwork->id, 'limit' => $limit, ], ]); } private function logSimilarityFailure(int $artworkId, Throwable $e): void { $gateway = $e instanceof VectorGatewayException ? $e : null; Log::warning('Vector similarity search failed', [ 'artwork_id' => $artworkId, 'route' => 'api.art.similar-ai', 'failure_stage' => $gateway?->operation ?? 'unknown', 'exception_class' => $e::class, 'http_status' => $gateway?->httpStatus, 'circuit_worthy' => $gateway?->circuitWorthy ?? false, 'circuit_open' => app(\App\Services\Vision\VectorGatewayClient::class)->circuitOpen(), ]); } }