Files
SkinbaseNova/config/recommendations.php

136 lines
7.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
return [
// Uses same queue family as vision jobs by default; keeps embedding work async and non-blocking.
'queue' => env('RECOMMENDATIONS_QUEUE', env('VISION_QUEUE', 'default')),
// ─── Phase 1 "For You" feed scoring weights ───────────────────────────────
// Influences the PHP reranking pass after Meilisearch candidate retrieval.
// Tweak here without code changes.
'weights' => [
// Tag overlap score weight (01 normalized overlap fraction)
'tag_overlap' => (float) env('RECO_W_TAG_OVERLAP', 0.40),
// Creator affinity score weight (1.0 if followed, 0 otherwise)
'creator_affinity' => (float) env('RECO_W_CREATOR_AFFINITY', 0.25),
// Popularity boost (log-normalised views/downloads)
'popularity' => (float) env('RECO_W_POPULARITY', 0.20),
// Freshness boost (exponential decay over 30 days)
'freshness' => (float) env('RECO_W_FRESHNESS', 0.15),
],
// ─── User preference signal weights ──────────────────────────────────────
// How much each user action contributes to building the reco profile.
'signal_weights' => [
'award' => (float) env('RECO_SIG_AWARD', 5.0),
'favorite' => (float) env('RECO_SIG_FAVORITE', 3.0),
'reaction' => (float) env('RECO_SIG_REACTION', 2.0),
'view' => (float) env('RECO_SIG_VIEW', 1.0),
'follow' => (float) env('RECO_SIG_FOLLOW', 2.0),
],
// ─── Candidate generation ──────────────────────────────────────────────────
// How many Meilisearch candidates to fetch before PHP reranking.
'candidate_pool_size' => (int) env('RECO_CANDIDATE_POOL', 200),
// ─── Diversity controls ────────────────────────────────────────────────────
// Maximum artworks per creator allowed in a single page of results.
'max_per_creator' => (int) env('RECO_MAX_PER_CREATOR', 3),
// Minimum distinct tag count in first 20 feed results.
'min_unique_tags' => (int) env('RECO_MIN_UNIQUE_TAGS', 5),
// ─── TTLs (seconds) ────────────────────────────────────────────────────────
'ttl' => [
// User reco profile cache (tag/creator affinity data)
'user_reco_profile' => (int) env('RECO_TTL_PROFILE', 6 * 3600),
// For You paginated results cache
'for_you_feed' => (int) env('RECO_TTL_FOR_YOU', 5 * 60),
// Similar artworks per artwork
'similar_artworks' => (int) env('RECO_TTL_SIMILAR', 30 * 60),
// Suggested creators per user
'creator_suggestions' => (int) env('RECO_TTL_CREATORS', 30 * 60),
// Suggested tags per user
'tag_suggestions' => (int) env('RECO_TTL_TAGS', 60 * 60),
],
// ─── Profile limits ────────────────────────────────────────────────────────
'profile' => [
'top_tags_limit' => (int) env('RECO_PROFILE_TAGS', 20),
'top_categories_limit' => (int) env('RECO_PROFILE_CATS', 5),
'strong_creators_limit' => (int) env('RECO_PROFILE_CREATORS', 50),
],
'embedding' => [
'enabled' => env('RECOMMENDATIONS_EMBEDDING_ENABLED', true),
'model' => env('RECOMMENDATIONS_EMBEDDING_MODEL', 'clip'),
'model_version' => env('RECOMMENDATIONS_EMBEDDING_MODEL_VERSION', 'v1'),
'algo_version' => env('RECOMMENDATIONS_ALGO_VERSION', 'clip-cosine-v1'),
// Preferred CLIP endpoint for embeddings. The service also accepts an embedding payload from the analyze endpoint response.
'endpoint' => env('CLIP_EMBED_ENDPOINT', '/embed'),
'timeout_seconds' => (int) env('CLIP_EMBED_TIMEOUT_SECONDS', 8),
'connect_timeout_seconds' => (int) env('CLIP_EMBED_CONNECT_TIMEOUT_SECONDS', 2),
'retries' => (int) env('CLIP_EMBED_HTTP_RETRIES', 1),
'retry_delay_ms' => (int) env('CLIP_EMBED_HTTP_RETRY_DELAY_MS', 200),
// Guardrails for malformed service responses.
'min_dim' => (int) env('RECOMMENDATIONS_MIN_DIM', 64),
'max_dim' => (int) env('RECOMMENDATIONS_MAX_DIM', 4096),
],
// Backfill chunk size for resumable queue fan-out.
'backfill_batch_size' => (int) env('RECOMMENDATIONS_BACKFILL_BATCH', 200),
// A/B support for recommendation ranking variants.
'ab' => [
'algo_versions' => array_values(array_filter(array_map(
static fn (string $value): string => trim($value),
explode(',', (string) env('RECOMMENDATIONS_AB_ALGO_VERSIONS', env('RECOMMENDATIONS_ALGO_VERSION', 'clip-cosine-v1')))
))),
],
// ─── Similar Artworks (hybrid recommender) ─────────────────────────────────
'similarity' => [
'model_version' => env('SIMILARITY_MODEL_VERSION', 'sim_v1'),
// Vector DB integration (behind feature flag)
'vector_enabled' => (bool) env('SIMILARITY_VECTOR_ENABLED', false),
'vector_adapter' => env('SIMILARITY_VECTOR_ADAPTER', 'pgvector'), // pgvector | pinecone
// Hybrid blend weights (spec §5.4)
'weights_with_vector' => [
'visual' => (float) env('SIM_W_VISUAL', 0.45),
'tag' => (float) env('SIM_W_TAG_VEC', 0.25),
'behavior' => (float) env('SIM_W_BEH_VEC', 0.20),
'category' => (float) env('SIM_W_CAT_VEC', 0.10),
],
'weights_without_vector' => [
'tag' => (float) env('SIM_W_TAG', 0.55),
'behavior' => (float) env('SIM_W_BEH', 0.35),
'category' => (float) env('SIM_W_CAT', 0.10),
],
// Diversity caps (spec §6)
'max_per_author' => (int) env('SIM_MAX_PER_AUTHOR', 2),
'result_limit' => (int) env('SIM_RESULT_LIMIT', 30),
'candidate_pool' => (int) env('SIM_CANDIDATE_POOL', 100),
'min_categories_top12' => (int) env('SIM_MIN_CATS_TOP12', 2),
// Behavior pair building
'user_favourites_cap' => (int) env('SIM_USER_FAV_CAP', 50),
// Cache TTL for precomputed lists (sec)
'cache_ttl' => (int) env('SIM_CACHE_TTL', 6 * 3600),
// Pinecone adapter settings
'pinecone' => [
'api_key' => env('PINECONE_API_KEY'),
'index_host' => env('PINECONE_INDEX_HOST'),
'index_name' => env('PINECONE_INDEX_NAME', 'skinbase-artworks'),
'namespace' => env('PINECONE_NAMESPACE', ''),
'top_k' => (int) env('PINECONE_TOP_K', 100),
],
],
];