const view = await serverClient.feeds.createFeedView({
view_id: "my_for_you_view",
activity_selectors: [{type: "following"}, {type: "popular"}],
ranking: { type: "interest", score: "interest_score * popularity * decay_exp(time)", defaults: {"popularity": 1} },
});
For You Feed
It’s now common for apps to default to a “For you” style feed instead of just a following feed. This section of the docs will explain how to build a for you feed with Stream.
While the exact logic differs per app, fundamently the “for you” feed does a 2 different things:
1 - Activity Selectors
The feed pulls in activities from different sources (activity selectors). Examples include:
- Popular activities
- Activities close to you
- Activities from people you follow
- Activities from your follow recommendations
- Specific editorial feeds
- Activities that match your interests
- Other activity queries/selections
Read more about the supported activity selectors here.
2- Ranking
After the feed combines the activities from different selectors it applies ranking. Stream supports expression based ranking out of the box. This allows you to define a formula to rank activities.
In the example below we’ll build a simple interest based for you feed
Step 1 - Create a feed group & view
For the feed view we’re enabling interest based ranking. We are ranking on interest_score * popularity * decay_exp(time)
For activity selectors we’re also keeping it simple, showing just the activities from people you follow & popular activities.
- Activity selector docs
- Ranking docs
We’re also setting up a feed group and enabling activity processors. The text topics activity processor uses an LLM to summarize the topics for an activity. It writes this data to activity.interest_tags. This update happens in the background.
const response = await serverClient.feeds.updateFeedGroup({
feed_group_id: "timeline",
default_view_id: "my_for_you_view",
activity_processors: [{type: "text_topics"}],
});
Step 2 - Activities with interest tags
Let’s add 2 activities to a feed
const feed = client.feed("user", "jack");
// since we've enabled the "text_topics" activity processor the "activity.interest_tags" will be automatically set
const response = await feed.addActivity({
type: "post",
text: "apple stock will go up",
});
// you can also set this field manually
const response = await client.addActivity({
fids: ["user:1", "stock:apple"],
type: "post",
text: "apple stock will go up",
interest_tags: ["apple", "stock"],
});
The important part here is that the activity has the interest tags set.
Step 3 - Reading the interest based ranked feed
When reading an interest based feed you can either have Stream automatically calculate the interest weights, or pass them manually.
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
limit: 10,
view: "my_for_you_view", // overwrite the default ranking or aggregation logic for this feed. good for split testing
});
Manually passing the interest weights
const interestWeights = {
"go": 1.0, // User loves Go
"python": 0.5, // User is ok with Python
"ruby": -1.0, // User dislikes Ruby
}
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
limit: 10,
view: "my_for_you_view",
interest_weights: interestWeights, // overwrite the default interest weights for this feed
});
Conclusion
In a few lines of code you created an interest based “for you” style feed. It includes popular content as well as activities from the people you follow. Activities from topics you engage with are shown higher in the feed.
This example kept things pretty basic but you can make it more complex.
- You could include activities based on their interest_tags. So if someone engages with content about snowboarding you could show them more of that
- Images can also be processed for interest tags
- More ranking & activity selectors can be added. Activities close to you, activities from follow suggestions etc.
You can build infinitely complex “for you” style feeds with ranking & activity selectors. Be sure to reach out to our support team in case anything is missing. We’re often adding selector or processors based on customer feedback.