gfsd
This commit is contained in:
@@ -2328,7 +2328,9 @@ const f0cklib = {
|
||||
mime,
|
||||
exclude_ids,
|
||||
session_tags = '',
|
||||
session_creators = ''
|
||||
session_creators = '',
|
||||
continuation = false,
|
||||
prefer_personalized = null
|
||||
} = {}) => {
|
||||
const ratingsArr = (Array.isArray(ratings) && ratings.length > 0) ? ratings : null;
|
||||
const modequery = computeBaseMode(mode, ratingsArr, session);
|
||||
@@ -2420,13 +2422,30 @@ const f0cklib = {
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Personalization vs Serendipity Split (60% personalized, 40% random exploration)
|
||||
// 2. Personalization vs Serendipity Split
|
||||
// In the sidebar suggestions:
|
||||
// - 1 of the first 3 recommendations is a personalized recommendation and the other 2 are not.
|
||||
// - After that, recommendations appear sporadically (~20% rate, non-consecutive with 3-5 random items between them).
|
||||
const isFirstBatch = !continuation && excludeItemIds.length === 0;
|
||||
let personalizedTarget;
|
||||
|
||||
if (maxLimit === 1) {
|
||||
// For single-card replacement: 65% chance personalized, 35% chance discovery
|
||||
personalizedTarget = Math.random() < 0.65 ? 1 : 0;
|
||||
if (prefer_personalized === true) {
|
||||
personalizedTarget = 1;
|
||||
} else if (prefer_personalized === false) {
|
||||
personalizedTarget = 0;
|
||||
} else {
|
||||
personalizedTarget = Math.random() < 0.20 ? 1 : 0;
|
||||
}
|
||||
} else if (isFirstBatch) {
|
||||
// 1 in top 3, plus sporadic recommendations in the remaining slots
|
||||
const topRec = maxLimit >= 1 ? 1 : 0;
|
||||
const remainingSlots = Math.max(0, maxLimit - 3);
|
||||
const sporadicRecs = Math.round(remainingSlots * 0.20);
|
||||
personalizedTarget = topRec + sporadicRecs;
|
||||
} else {
|
||||
personalizedTarget = Math.round(maxLimit * 0.60);
|
||||
// Continuation batch: sporadic recommendations throughout
|
||||
personalizedTarget = Math.max(1, Math.round(maxLimit * 0.20));
|
||||
}
|
||||
|
||||
const targetTagIds = targetTagEntries.map(([tid]) => tid);
|
||||
@@ -2537,8 +2556,9 @@ const f0cklib = {
|
||||
let randomItems = [];
|
||||
if (neededRandom > 0) {
|
||||
const allExclude = [...excludeItemIds, ...personalizedItems.map(p => p.id)];
|
||||
// Fetch extra buffer of random items to ensure ample spacing
|
||||
randomItems = await f0cklib.getRandomRecommendations({
|
||||
limit: neededRandom,
|
||||
limit: Math.max(neededRandom + 3, maxLimit),
|
||||
mode,
|
||||
ratings,
|
||||
session,
|
||||
@@ -2550,14 +2570,74 @@ const f0cklib = {
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Combine & Interweave with Fisher-Yates Shuffle
|
||||
const combined = [...personalizedItems, ...randomItems];
|
||||
for (let i = combined.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[combined[i], combined[j]] = [combined[j], combined[i]];
|
||||
// 4. Combine & Interweave
|
||||
if (maxLimit === 1) {
|
||||
return personalizedItems.length > 0 ? personalizedItems.slice(0, 1) : randomItems.slice(0, 1);
|
||||
}
|
||||
|
||||
return combined.slice(0, maxLimit);
|
||||
if (personalizedItems.length === 0) {
|
||||
return randomItems.slice(0, maxLimit);
|
||||
}
|
||||
|
||||
if (randomItems.length === 0) {
|
||||
return personalizedItems.slice(0, maxLimit);
|
||||
}
|
||||
|
||||
const result = [];
|
||||
if (isFirstBatch) {
|
||||
// First 3 items: exactly 1 is a recommendation, and the other 2 are not
|
||||
const topCount = Math.min(3, maxLimit);
|
||||
const topSlots = new Array(topCount);
|
||||
const recSlot = Math.floor(Math.random() * topCount);
|
||||
topSlots[recSlot] = personalizedItems.shift();
|
||||
|
||||
for (let s = 0; s < topCount; s++) {
|
||||
if (s !== recSlot) {
|
||||
topSlots[s] = randomItems.length > 0 ? randomItems.shift() : personalizedItems.shift();
|
||||
}
|
||||
}
|
||||
for (let s = 0; s < topCount; s++) {
|
||||
if (topSlots[s]) result.push(topSlots[s]);
|
||||
}
|
||||
|
||||
// Remaining slots: recommendations come sporadically (separated by 3 to 5 non-recommendations)
|
||||
let gapSinceRec = topCount - 1 - recSlot;
|
||||
let targetGap = Math.floor(Math.random() * 3) + 3; // 3, 4, or 5 random items
|
||||
|
||||
while (result.length < maxLimit && (personalizedItems.length > 0 || randomItems.length > 0)) {
|
||||
if (personalizedItems.length > 0 && gapSinceRec >= targetGap && randomItems.length > 0) {
|
||||
result.push(personalizedItems.shift());
|
||||
gapSinceRec = 0;
|
||||
targetGap = Math.floor(Math.random() * 3) + 3;
|
||||
} else if (randomItems.length > 0) {
|
||||
result.push(randomItems.shift());
|
||||
gapSinceRec++;
|
||||
} else if (personalizedItems.length > 0) {
|
||||
result.push(personalizedItems.shift());
|
||||
gapSinceRec = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Continuation batch: recommendations come sporadically throughout
|
||||
let gapSinceRec = Math.floor(Math.random() * 2) + 1;
|
||||
let targetGap = Math.floor(Math.random() * 3) + 3;
|
||||
|
||||
while (result.length < maxLimit && (personalizedItems.length > 0 || randomItems.length > 0)) {
|
||||
if (personalizedItems.length > 0 && gapSinceRec >= targetGap && randomItems.length > 0) {
|
||||
result.push(personalizedItems.shift());
|
||||
gapSinceRec = 0;
|
||||
targetGap = Math.floor(Math.random() * 3) + 3;
|
||||
} else if (randomItems.length > 0) {
|
||||
result.push(randomItems.shift());
|
||||
gapSinceRec++;
|
||||
} else if (personalizedItems.length > 0) {
|
||||
result.push(personalizedItems.shift());
|
||||
gapSinceRec = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.slice(0, maxLimit);
|
||||
},
|
||||
|
||||
getTagFeedItems: async ({
|
||||
|
||||
Reference in New Issue
Block a user