Upload beautify
This commit is contained in:
95
app/Services/Vision/ArtworkEmbeddingClient.php
Normal file
95
app/Services/Vision/ArtworkEmbeddingClient.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Vision;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
final class ArtworkEmbeddingClient
|
||||
{
|
||||
/**
|
||||
* @return array<int, float>
|
||||
*/
|
||||
public function embed(string $imageUrl, int $artworkId, string $sourceHash): array
|
||||
{
|
||||
$base = trim((string) config('vision.clip.base_url', ''));
|
||||
if ($base === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$endpoint = (string) config('recommendations.embedding.endpoint', '/embed');
|
||||
$url = rtrim($base, '/') . '/' . ltrim($endpoint, '/');
|
||||
|
||||
$timeout = (int) config('recommendations.embedding.timeout_seconds', 8);
|
||||
$connectTimeout = (int) config('recommendations.embedding.connect_timeout_seconds', 2);
|
||||
$retries = (int) config('recommendations.embedding.retries', 1);
|
||||
$delay = (int) config('recommendations.embedding.retry_delay_ms', 200);
|
||||
|
||||
$response = Http::acceptJson()
|
||||
->connectTimeout(max(1, $connectTimeout))
|
||||
->timeout(max(1, $timeout))
|
||||
->retry(max(0, $retries), max(0, $delay), throw: false)
|
||||
->post($url, [
|
||||
'image_url' => $imageUrl,
|
||||
'artwork_id' => $artworkId,
|
||||
'hash' => $sourceHash,
|
||||
]);
|
||||
|
||||
if (! $response->ok()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->extractEmbedding($response->json());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $json
|
||||
* @return array<int, float>
|
||||
*/
|
||||
private function extractEmbedding(mixed $json): array
|
||||
{
|
||||
$candidate = null;
|
||||
|
||||
if (is_array($json) && $this->isNumericVector($json)) {
|
||||
$candidate = $json;
|
||||
} elseif (is_array($json) && isset($json['embedding']) && is_array($json['embedding'])) {
|
||||
$candidate = $json['embedding'];
|
||||
} elseif (is_array($json) && isset($json['data']['embedding']) && is_array($json['data']['embedding'])) {
|
||||
$candidate = $json['data']['embedding'];
|
||||
}
|
||||
|
||||
if (! is_array($candidate) || ! $this->isNumericVector($candidate)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$vector = array_map(static fn ($value): float => (float) $value, $candidate);
|
||||
$dim = count($vector);
|
||||
|
||||
$minDim = (int) config('recommendations.embedding.min_dim', 64);
|
||||
$maxDim = (int) config('recommendations.embedding.max_dim', 4096);
|
||||
if ($dim < $minDim || $dim > $maxDim) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $arr
|
||||
*/
|
||||
private function isNumericVector(array $arr): bool
|
||||
{
|
||||
if ($arr === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($arr as $value) {
|
||||
if (! is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user