77 lines
2.8 KiB
PHP
77 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Recommendations;
|
|
|
|
final class RecommendationFeedResolver
|
|
{
|
|
public function __construct(
|
|
private readonly PersonalizedFeedService $personalizedFeed,
|
|
private readonly \App\Services\Recommendations\RecommendationServiceV2 $v2Feed,
|
|
) {
|
|
}
|
|
|
|
public function getFeed(int $userId, int $limit = 24, ?string $cursor = null, ?string $algoVersion = null): array
|
|
{
|
|
if ($this->shouldUseV2($userId, $algoVersion)) {
|
|
return $this->v2Feed->getFeed($userId, $limit, $cursor, $algoVersion);
|
|
}
|
|
|
|
return $this->personalizedFeed->getFeed($userId, $limit, $cursor, $algoVersion);
|
|
}
|
|
|
|
public function regenerateCacheForUser(int $userId, ?string $algoVersion = null): void
|
|
{
|
|
if ($this->shouldUseV2($userId, $algoVersion)) {
|
|
$this->v2Feed->regenerateCacheForUser($userId, $algoVersion);
|
|
return;
|
|
}
|
|
|
|
$this->personalizedFeed->regenerateCacheForUser($userId, $algoVersion);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function inspectDecision(int $userId, ?string $algoVersion = null): array
|
|
{
|
|
$requested = trim((string) ($algoVersion ?? ''));
|
|
$v2AlgoVersion = trim((string) config('discovery.v2.algo_version', 'clip-cosine-v2-adaptive'));
|
|
$v2Enabled = (bool) config('discovery.v2.enabled', false);
|
|
$rollout = max(0, min(100, (int) config('discovery.v2.rollout_percentage', 0)));
|
|
$bucket = abs((int) crc32((string) $userId)) % 100;
|
|
$forcedByAlgoVersion = $requested !== '' && $requested === $v2AlgoVersion;
|
|
$usesV2 = $forcedByAlgoVersion
|
|
|| ($v2Enabled && ($rollout >= 100 || ($rollout > 0 && $bucket < $rollout)));
|
|
|
|
$reason = match (true) {
|
|
$forcedByAlgoVersion => 'explicit_algo_override',
|
|
! $v2Enabled => 'v2_disabled',
|
|
$rollout >= 100 => 'full_rollout',
|
|
$rollout <= 0 => 'rollout_zero',
|
|
$bucket < $rollout => 'bucket_in_rollout',
|
|
default => 'bucket_outside_rollout',
|
|
};
|
|
|
|
return [
|
|
'user_id' => $userId,
|
|
'requested_algo_version' => $requested !== '' ? $requested : null,
|
|
'v2_algo_version' => $v2AlgoVersion,
|
|
'v2_enabled' => $v2Enabled,
|
|
'rollout_percentage' => $rollout,
|
|
'bucket' => $bucket,
|
|
'bucket_in_rollout' => $bucket < $rollout,
|
|
'forced_by_algo_version' => $forcedByAlgoVersion,
|
|
'uses_v2' => $usesV2,
|
|
'selected_engine' => $usesV2 ? 'v2' : 'v1',
|
|
'reason' => $reason,
|
|
];
|
|
}
|
|
|
|
private function shouldUseV2(int $userId, ?string $algoVersion = null): bool
|
|
{
|
|
return (bool) ($this->inspectDecision($userId, $algoVersion)['uses_v2'] ?? false);
|
|
}
|
|
}
|