mirror of
https://github.com/twitter/the-algorithm.git
synced 2025-01-12 12:19:10 +01:00
[docx] split commit for file 1600
Signed-off-by: Ari Archer <ari.web.xyz@gmail.com>
This commit is contained in:
parent
e27b2e31c3
commit
94812b2b2c
Binary file not shown.
@ -1,70 +0,0 @@
|
|||||||
package com.twitter.home_mixer.module
|
|
||||||
|
|
||||||
import com.google.inject.name.Named
|
|
||||||
import com.google.inject.Provides
|
|
||||||
import com.twitter.conversions.DurationOps.RichDuration
|
|
||||||
import com.twitter.finagle.mtls.authentication.ServiceIdentifier
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.home_mixer.param.HomeMixerInjectionNames.TweetypieStaticEntitiesCache
|
|
||||||
import com.twitter.inject.TwitterModule
|
|
||||||
import com.twitter.product_mixer.shared_library.memcached_client.MemcachedClientBuilder
|
|
||||||
import com.twitter.servo.cache.FinagleMemcache
|
|
||||||
import com.twitter.servo.cache.KeyTransformer
|
|
||||||
import com.twitter.servo.cache.KeyValueTransformingTtlCache
|
|
||||||
import com.twitter.servo.cache.ObservableTtlCache
|
|
||||||
import com.twitter.servo.cache.Serializer
|
|
||||||
import com.twitter.servo.cache.ThriftSerializer
|
|
||||||
import com.twitter.servo.cache.TtlCache
|
|
||||||
import com.twitter.tweetypie.{thriftscala => tp}
|
|
||||||
import javax.inject.Singleton
|
|
||||||
import org.apache.thrift.protocol.TCompactProtocol
|
|
||||||
|
|
||||||
object TweetypieStaticEntitiesCacheClientModule extends TwitterModule {
|
|
||||||
|
|
||||||
private val ScopeName = "TweetypieStaticEntitiesMemcache"
|
|
||||||
private val ProdDest = "/srv#/prod/local/cache/timelinescorer_tweet_core_data:twemcaches"
|
|
||||||
|
|
||||||
private val tweetsSerializer: Serializer[tp.Tweet] = {
|
|
||||||
new ThriftSerializer[tp.Tweet](tp.Tweet, new TCompactProtocol.Factory())
|
|
||||||
}
|
|
||||||
private val keyTransformer: KeyTransformer[Long] = { tweetId => tweetId.toString }
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named(TweetypieStaticEntitiesCache)
|
|
||||||
def providesTweetypieStaticEntitiesCache(
|
|
||||||
statsReceiver: StatsReceiver,
|
|
||||||
serviceIdentifier: ServiceIdentifier
|
|
||||||
): TtlCache[Long, tp.Tweet] = {
|
|
||||||
val memCacheClient = MemcachedClientBuilder.buildMemcachedClient(
|
|
||||||
destName = ProdDest,
|
|
||||||
numTries = 1,
|
|
||||||
numConnections = 1,
|
|
||||||
requestTimeout = 50.milliseconds,
|
|
||||||
globalTimeout = 100.milliseconds,
|
|
||||||
connectTimeout = 100.milliseconds,
|
|
||||||
acquisitionTimeout = 100.milliseconds,
|
|
||||||
serviceIdentifier = serviceIdentifier,
|
|
||||||
statsReceiver = statsReceiver
|
|
||||||
)
|
|
||||||
mkCache(new FinagleMemcache(memCacheClient), statsReceiver)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def mkCache(
|
|
||||||
finagleMemcache: FinagleMemcache,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): TtlCache[Long, tp.Tweet] = {
|
|
||||||
val baseCache: KeyValueTransformingTtlCache[Long, String, tp.Tweet, Array[Byte]] =
|
|
||||||
new KeyValueTransformingTtlCache(
|
|
||||||
underlyingCache = finagleMemcache,
|
|
||||||
transformer = tweetsSerializer,
|
|
||||||
underlyingKey = keyTransformer
|
|
||||||
)
|
|
||||||
ObservableTtlCache(
|
|
||||||
underlyingCache = baseCache,
|
|
||||||
statsReceiver = statsReceiver.scope(ScopeName),
|
|
||||||
windowSize = 1000,
|
|
||||||
name = ScopeName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,10 +0,0 @@
|
|||||||
package com.twitter.home_mixer.param
|
|
||||||
|
|
||||||
import com.twitter.inject.TwitterModule
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.registry.GlobalParamConfig
|
|
||||||
|
|
||||||
object GlobalParamConfigModule extends TwitterModule {
|
|
||||||
override def configure(): Unit = {
|
|
||||||
bind[GlobalParamConfig].to[HomeGlobalParamConfig]
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,34 +0,0 @@
|
|||||||
package com.twitter.home_mixer.param
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams._
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.registry.GlobalParamConfig
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register Params that do not relate to a specific product. See GlobalParamConfig -> ParamConfig
|
|
||||||
* for hooks to register Params based on type.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
class HomeGlobalParamConfig @Inject() () extends GlobalParamConfig {
|
|
||||||
|
|
||||||
override val booleanFSOverrides = Seq(
|
|
||||||
AdsDisableInjectionBasedOnUserRoleParam,
|
|
||||||
EnableAdvertiserBrandSafetySettingsFeatureHydratorParam,
|
|
||||||
EnableImpressionBloomFilter,
|
|
||||||
EnableNahFeedbackInfoParam,
|
|
||||||
EnableNewTweetsPillAvatarsParam,
|
|
||||||
EnableScribeServedCandidatesParam,
|
|
||||||
EnableSendScoresToClient,
|
|
||||||
EnableSocialContextParam,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val boundedIntFSOverrides = Seq(
|
|
||||||
MaxNumberReplaceInstructionsParam,
|
|
||||||
TimelinesPersistenceStoreMaxEntriesPerClient,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val boundedDoubleFSOverrides = Seq(
|
|
||||||
ImpressionBloomFilterFalsePositiveRateParam
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,86 +0,0 @@
|
|||||||
package com.twitter.home_mixer.param
|
|
||||||
|
|
||||||
import com.twitter.timelines.configapi.FSBoundedParam
|
|
||||||
import com.twitter.timelines.configapi.FSParam
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiate Params that do not relate to a specific product.
|
|
||||||
*
|
|
||||||
* @see [[com.twitter.product_mixer.core.product.ProductParamConfig.supportedClientFSName]]
|
|
||||||
*/
|
|
||||||
object HomeGlobalParams {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This param is used to disable ads injection for timelines served by home-mixer.
|
|
||||||
* It is currently used to maintain user-role based no-ads lists for automation accounts,
|
|
||||||
* and should NOT be used for other purposes.
|
|
||||||
*/
|
|
||||||
object AdsDisableInjectionBasedOnUserRoleParam
|
|
||||||
extends FSParam("home_mixer_ads_disable_injection_based_on_user_role", false)
|
|
||||||
|
|
||||||
object EnableSendScoresToClient
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_enable_send_scores_to_client",
|
|
||||||
default = false
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableNahFeedbackInfoParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_enable_nah_feedback_info",
|
|
||||||
default = false
|
|
||||||
)
|
|
||||||
|
|
||||||
object MaxNumberReplaceInstructionsParam
|
|
||||||
extends FSBoundedParam[Int](
|
|
||||||
name = "home_mixer_max_number_replace_instructions",
|
|
||||||
default = 100,
|
|
||||||
min = 0,
|
|
||||||
max = 200
|
|
||||||
)
|
|
||||||
|
|
||||||
object TimelinesPersistenceStoreMaxEntriesPerClient
|
|
||||||
extends FSBoundedParam[Int](
|
|
||||||
name = "home_mixer_timelines_persistence_store_max_entries_per_client",
|
|
||||||
default = 1800,
|
|
||||||
min = 500,
|
|
||||||
max = 5000
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableNewTweetsPillAvatarsParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_enable_new_tweets_pill_avatars",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableSocialContextParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_enable_social_context",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableAdvertiserBrandSafetySettingsFeatureHydratorParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_enable_advertiser_brand_safety_settings_feature_hydrator",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableImpressionBloomFilter
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_enable_impression_bloom_filter",
|
|
||||||
default = false
|
|
||||||
)
|
|
||||||
|
|
||||||
object ImpressionBloomFilterFalsePositiveRateParam
|
|
||||||
extends FSBoundedParam[Double](
|
|
||||||
name = "home_mixer_impression_bloom_filter_false_positive_rate",
|
|
||||||
default = 0.005,
|
|
||||||
min = 0.001,
|
|
||||||
max = 0.01
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableScribeServedCandidatesParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "home_mixer_served_tweets_enable_scribing",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,13 +0,0 @@
|
|||||||
package com.twitter.home_mixer.param
|
|
||||||
|
|
||||||
object HomeMixerFlagName {
|
|
||||||
final val ScribeClientEventsFlag = "scribe.client_events"
|
|
||||||
final val ScribeServedCandidatesFlag = "scribe.served_candidates"
|
|
||||||
final val ScribeScoredCandidatesFlag = "scribe.scored_candidates"
|
|
||||||
final val ScribeServedCommonFeaturesAndCandidateFeaturesFlag =
|
|
||||||
"scribe.served_common_features_and_candidate_features"
|
|
||||||
final val DataRecordMetadataStoreConfigsYmlFlag = "data.record.metadata.store.configs.yml"
|
|
||||||
final val DarkTrafficFilterDeciderKey = "thrift.dark.traffic.filter.decider_key"
|
|
||||||
final val TargetFetchLatency = "target.fetch.latency"
|
|
||||||
final val TargetScoringLatency = "target.scoring.latency"
|
|
||||||
}
|
|
Binary file not shown.
@ -1,46 +0,0 @@
|
|||||||
package com.twitter.home_mixer.param
|
|
||||||
|
|
||||||
object HomeMixerInjectionNames {
|
|
||||||
final val AuthorFeatureRepository = "AuthorFeatureRepository"
|
|
||||||
final val CandidateFeaturesScribeEventPublisher = "CandidateFeaturesScribeEventPublisher"
|
|
||||||
final val CommonFeaturesScribeEventPublisher = "CommonFeaturesScribeEventPublisher"
|
|
||||||
final val EarlybirdRepository = "EarlybirdRepository"
|
|
||||||
final val EngagementsReceivedByAuthorCache = "EngagementsReceivedByAuthorCache"
|
|
||||||
final val GraphTwoHopRepository = "GraphTwoHopRepository"
|
|
||||||
final val HomeAuthorFeaturesCacheClient = "HomeAuthorFeaturesCacheClient"
|
|
||||||
final val InterestsThriftServiceClient = "InterestsThriftServiceClient"
|
|
||||||
final val BatchedStratoClientWithModerateTimeout = "BatchedStratoClientWithModerateTimeout"
|
|
||||||
final val ManhattanApolloClient = "ManhattanApolloClient"
|
|
||||||
final val ManhattanAthenaClient = "ManhattanAthenaClient"
|
|
||||||
final val ManhattanOmegaClient = "ManhattanOmegaClient"
|
|
||||||
final val ManhattanStarbuckClient = "ManhattanStarbuckClient"
|
|
||||||
final val MetricCenterUserCountingFeatureRepository = "MetricCenterUserCountingFeatureRepository"
|
|
||||||
final val MinimumFeaturesScribeEventPublisher = "MinimumFeaturesScribeEventPublisher"
|
|
||||||
final val RealGraphInNetworkScores = "RealGraphInNetworkScores"
|
|
||||||
final val RealGraphManhattanEndpoint = "RealGraphFeaturesManhattanEndpoint"
|
|
||||||
final val RealGraphFeatureRepository = "RealGraphFeatureRepository"
|
|
||||||
final val RealTimeInteractionGraphUserVertexCache = "RealTimeInteractionGraphUserVertexCache"
|
|
||||||
final val RealTimeInteractionGraphUserVertexClient = "RealTimeInteractionGraphUserVertexClient"
|
|
||||||
final val StaleTweetsCache = "StaleTweetsCache"
|
|
||||||
final val TimelineAggregateMetadataRepository = "TimelineAggregateMetadataRepository"
|
|
||||||
final val TimelineAggregatePartARepository = "TimelineAggregatePartARepository"
|
|
||||||
final val TimelineAggregatePartBRepository = "TimelineAggregatePartBRepository"
|
|
||||||
final val TimelinesRealTimeAggregateClient = "TimelinesRealTimeAggregateClient"
|
|
||||||
final val TopicCountryEngagementCache = "TopicCountryEngagementCache"
|
|
||||||
final val TopicEngagementCache = "TopicEngagementCache"
|
|
||||||
final val TweetCountryEngagementCache = "TweetCountryEngagementCache"
|
|
||||||
final val TweetEngagementCache = "TweetEngagementCache"
|
|
||||||
final val TweetypieContentRepository = "TweetypieContentRepository"
|
|
||||||
final val TweetypieStaticEntitiesCache = "TweetypieStaticEntitiesCache"
|
|
||||||
final val TwhinAuthorFollowFeatureCacheClient = "TwhinAuthorFollowFeatureCacheClient"
|
|
||||||
final val TwhinAuthorFollowFeatureRepository = "TwhinAuthorFollowFeatureRepository"
|
|
||||||
final val TwhinUserEngagementFeatureRepository = "TwhinUserEngagementFeatureRepository"
|
|
||||||
final val TwhinUserFollowFeatureRepository = "TwhinUserFollowFeatureRepository"
|
|
||||||
final val TwitterListEngagementCache = "TwitterListEngagementCache"
|
|
||||||
final val UserAuthorEngagementCache = "UserAuthorEngagementCache"
|
|
||||||
final val UserEngagementCache = "UserEngagementCache"
|
|
||||||
final val UserFollowedTopicIdsRepository = "UserFollowedTopicIdsRepository"
|
|
||||||
final val UserLanguagesRepository = "UserLanguagesRepository"
|
|
||||||
final val UserTopicEngagementForNewUserCache = "UserTopicEngagementForNewUserCache"
|
|
||||||
final val UtegSocialProofRepository = "UtegSocialProofRepository"
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"servo/decider",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,52 +0,0 @@
|
|||||||
package com.twitter.home_mixer.param.decider
|
|
||||||
|
|
||||||
import com.twitter.servo.decider.DeciderKeyEnum
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These values must correspond to the deciders configured in the
|
|
||||||
* home-mixer/server/src/main/resources/config/decider.yml file
|
|
||||||
*
|
|
||||||
* @see [[com.twitter.product_mixer.core.product.ProductParamConfig.enabledDeciderKey]]
|
|
||||||
*/
|
|
||||||
object DeciderKey extends DeciderKeyEnum {
|
|
||||||
// Products
|
|
||||||
val EnableForYouProduct = Value("enable_for_you_product")
|
|
||||||
|
|
||||||
val EnableFollowingProduct = Value("enable_following_product")
|
|
||||||
|
|
||||||
val EnableScoredTweetsProduct = Value("enable_scored_tweets_product")
|
|
||||||
|
|
||||||
val EnableListTweetsProduct = Value("enable_list_tweets_product")
|
|
||||||
|
|
||||||
val EnableListRecommendedUsersProduct = Value("enable_list_recommended_users_product")
|
|
||||||
|
|
||||||
val EnableSubscribedProduct = Value("enable_subscribed_product")
|
|
||||||
|
|
||||||
// Candidate Pipelines
|
|
||||||
val EnableForYouScoredTweetsCandidatePipeline =
|
|
||||||
Value("enable_for_you_scored_tweets_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsTweetMixerCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_tweet_mixer_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsInNetworkCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_in_network_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsUtegCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_uteg_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsFrsCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_frs_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsListsCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_lists_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsPopularVideosCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_popular_videos_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableScoredTweetsBackfillCandidatePipeline =
|
|
||||||
Value("enable_scored_tweets_backfill_candidate_pipeline")
|
|
||||||
|
|
||||||
val EnableSimClustersSimilarityFeatureHydration =
|
|
||||||
Value("enable_simclusters_similarity_feature_hydration")
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/javax/inject:javax.inject",
|
|
||||||
"finatra/inject/inject-core/src/main/scala",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model/request",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/list_recommended_users",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/list_recommended_users/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/list_tweets",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/list_tweets/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/scored_tweets",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/scored_tweets/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/subscribed",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/subscribed/model",
|
|
||||||
"home-mixer/thrift/src/main/thrift:thrift-scala",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/query/ads",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product/guice",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product/registry",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,11 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product
|
|
||||||
|
|
||||||
import com.twitter.inject.TwitterModule
|
|
||||||
import com.twitter.product_mixer.core.product.registry.ProductPipelineRegistryConfig
|
|
||||||
|
|
||||||
object HomeMixerProductModule extends TwitterModule {
|
|
||||||
|
|
||||||
override def configure(): Unit = {
|
|
||||||
bind[ProductPipelineRegistryConfig].to[HomeProductPipelineRegistryConfig]
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,60 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.model.request.FollowingProduct
|
|
||||||
import com.twitter.home_mixer.model.request.ForYouProduct
|
|
||||||
import com.twitter.home_mixer.model.request.ListRecommendedUsersProduct
|
|
||||||
import com.twitter.home_mixer.model.request.ListTweetsProduct
|
|
||||||
import com.twitter.home_mixer.model.request.ScoredTweetsProduct
|
|
||||||
import com.twitter.home_mixer.model.request.SubscribedProduct
|
|
||||||
import com.twitter.home_mixer.product.following.FollowingProductPipelineConfig
|
|
||||||
import com.twitter.home_mixer.product.for_you.ForYouProductPipelineConfig
|
|
||||||
import com.twitter.home_mixer.product.list_recommended_users.ListRecommendedUsersProductPipelineConfig
|
|
||||||
import com.twitter.home_mixer.product.scored_tweets.ScoredTweetsProductPipelineConfig
|
|
||||||
import com.twitter.home_mixer.product.list_tweets.ListTweetsProductPipelineConfig
|
|
||||||
import com.twitter.home_mixer.product.subscribed.SubscribedProductPipelineConfig
|
|
||||||
import com.twitter.inject.Injector
|
|
||||||
import com.twitter.product_mixer.core.product.guice.ProductScope
|
|
||||||
import com.twitter.product_mixer.core.product.registry.ProductPipelineRegistryConfig
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class HomeProductPipelineRegistryConfig @Inject() (
|
|
||||||
injector: Injector,
|
|
||||||
productScope: ProductScope)
|
|
||||||
extends ProductPipelineRegistryConfig {
|
|
||||||
|
|
||||||
private val followingProductPipelineConfig = productScope.let(FollowingProduct) {
|
|
||||||
injector.instance[FollowingProductPipelineConfig]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val forYouProductPipelineConfig = productScope.let(ForYouProduct) {
|
|
||||||
injector.instance[ForYouProductPipelineConfig]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val scoredTweetsProductPipelineConfig = productScope.let(ScoredTweetsProduct) {
|
|
||||||
injector.instance[ScoredTweetsProductPipelineConfig]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val listTweetsProductPipelineConfig = productScope.let(ListTweetsProduct) {
|
|
||||||
injector.instance[ListTweetsProductPipelineConfig]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val listRecommendedUsersProductPipelineConfig =
|
|
||||||
productScope.let(ListRecommendedUsersProduct) {
|
|
||||||
injector.instance[ListRecommendedUsersProductPipelineConfig]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val subscribedProductPipelineConfig = productScope.let(SubscribedProduct) {
|
|
||||||
injector.instance[SubscribedProductPipelineConfig]
|
|
||||||
}
|
|
||||||
|
|
||||||
override val productPipelineConfigs = Seq(
|
|
||||||
followingProductPipelineConfig,
|
|
||||||
forYouProductPipelineConfig,
|
|
||||||
scoredTweetsProductPipelineConfig,
|
|
||||||
listTweetsProductPipelineConfig,
|
|
||||||
listRecommendedUsersProductPipelineConfig,
|
|
||||||
subscribedProductPipelineConfig,
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/javax/inject:javax.inject",
|
|
||||||
"ads-injection/lib/src/main/scala/com/twitter/goldfinch/api",
|
|
||||||
"finagle/finagle-memcached/src/main/scala",
|
|
||||||
"finatra/inject/inject-core/src/main/scala",
|
|
||||||
"finatra/inject/inject-core/src/main/scala/com/twitter/inject",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/candidate_pipeline",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/candidate_source",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/decorator",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/decorator/builder",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/decorator/urt/builder",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/feature_hydrator",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/filter",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/gate",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/selector",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/side_effect",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/marshaller/timelines",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model/request",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/param",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following/param",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/service",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/util",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/account_recommendations_mixer",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/hermit",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/tweetconvosvc",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/decorator/urt",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/param_gated",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/async",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/impressed_tweets",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/param_gated",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/social_graph",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/filter",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/gate",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/candidate",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/candidate/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/presentation/urt",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/flexible_injection_pipeline",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/who_to_follow_module",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/premarshaller/urt",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/selector",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/selector/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/side_effect",
|
|
||||||
"product-mixer/core/src/main/java/com/twitter/product_mixer/core/product/guice/scope",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/configapi",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/decorator",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/decorator/urt/builder",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/marshaller/response/urt",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/common/presentation",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/marshalling/request",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/marshalling/response/urt",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/marshalling/response/urt/item",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/candidate",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/mixer",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/product",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product/guice",
|
|
||||||
"src/java/com/twitter/search/common/schema/base",
|
|
||||||
"src/java/com/twitter/search/common/schema/earlybird",
|
|
||||||
"src/java/com/twitter/search/common/util/lang",
|
|
||||||
"src/java/com/twitter/search/queryparser/query:core-query-nodes",
|
|
||||||
"src/java/com/twitter/search/queryparser/query/search:search-query-nodes",
|
|
||||||
"src/scala/com/twitter/suggests/controller_data",
|
|
||||||
"src/thrift/com/twitter/search:earlybird-scala",
|
|
||||||
"src/thrift/com/twitter/search/common:constants-java",
|
|
||||||
"src/thrift/com/twitter/suggests/controller_data:controller_data-scala",
|
|
||||||
"src/thrift/com/twitter/timelinemixer:thrift-scala",
|
|
||||||
"src/thrift/com/twitter/timelines/render:thrift-scala",
|
|
||||||
"src/thrift/com/twitter/timelinescorer:thrift-scala",
|
|
||||||
"src/thrift/com/twitter/timelinescorer/common/scoredtweetcandidate:thrift-scala",
|
|
||||||
"src/thrift/com/twitter/timelinescorer/server/internal:thrift-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:service-scala",
|
|
||||||
"stitch/stitch-gizmoduck",
|
|
||||||
"stitch/stitch-tweetypie",
|
|
||||||
"stringcenter/client",
|
|
||||||
"stringcenter/client/src/main/java",
|
|
||||||
"timelinemixer/server/src/main/scala/com/twitter/timelinemixer/injection/model/candidate",
|
|
||||||
"timelines/src/main/scala/com/twitter/timelines/clients/relevance_search",
|
|
||||||
"timelines/src/main/scala/com/twitter/timelines/injection/scribe",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following/param",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/decorator/urt",
|
|
||||||
"src/thrift/com/twitter/timelines/render:thrift-scala",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,108 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following
|
|
||||||
|
|
||||||
import com.twitter.adserver.{thriftscala => ads}
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeAdsClientEventDetailsBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.ExcludeSoftUserGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TweetLanguageFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TweetTextFeature
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.EnableAdvertiserBrandSafetySettingsFeatureHydratorParam
|
|
||||||
import com.twitter.home_mixer.product.following.model.FollowingQuery
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.EnableAdsCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.EnableFastAds
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.ads.AdsProdThriftCandidateSource
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.contextual_ref.ContextualTweetRefBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.ad.AdsCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.metadata.ClientEventInfoBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.candidate.ads.AdvertiserBrandSafetySettingsFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.candidate.param_gated.ParamGatedCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptyCandidatesGate
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.ads.AdsCandidate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.AdsDependentCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.AdsDependentCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.CountCandidatesFromPipelines
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.PipelineScopedOrganicItems
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.ValidAdImpressionIdFilter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.CandidateScope
|
|
||||||
import com.twitter.product_mixer.core.gate.ParamNotGate
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.rtf.safety_level.TimelineHomePromotedHydrationSafetyLevel
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.contextual_ref.TweetHydrationContext
|
|
||||||
import com.twitter.timelines.injection.scribe.InjectionScribeUtil
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => st}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class FollowingAdsCandidatePipelineBuilder @Inject() (
|
|
||||||
adsCandidatePipelineConfigBuilder: AdsDependentCandidatePipelineConfigBuilder,
|
|
||||||
adsCandidateSource: AdsProdThriftCandidateSource,
|
|
||||||
advertiserBrandSafetySettingsFeatureHydrator: AdvertiserBrandSafetySettingsFeatureHydrator[
|
|
||||||
FollowingQuery,
|
|
||||||
AdsCandidate
|
|
||||||
]) {
|
|
||||||
|
|
||||||
private val identifier: CandidatePipelineIdentifier = CandidatePipelineIdentifier("FollowingAds")
|
|
||||||
|
|
||||||
private val suggestType = st.SuggestType.Promoted
|
|
||||||
|
|
||||||
private val clientEventInfoBuilder = ClientEventInfoBuilder(
|
|
||||||
component = InjectionScribeUtil.scribeComponent(suggestType).get,
|
|
||||||
detailsBuilder = Some(HomeAdsClientEventDetailsBuilder(Some(suggestType.name)))
|
|
||||||
)
|
|
||||||
|
|
||||||
private val contextualTweetRefBuilder = ContextualTweetRefBuilder(
|
|
||||||
TweetHydrationContext(
|
|
||||||
safetyLevelOverride = Some(TimelineHomePromotedHydrationSafetyLevel),
|
|
||||||
outerTweetContext = None
|
|
||||||
))
|
|
||||||
|
|
||||||
private val decorator = UrtItemCandidateDecorator(
|
|
||||||
AdsCandidateUrtItemBuilder(
|
|
||||||
tweetClientEventInfoBuilder = Some(clientEventInfoBuilder),
|
|
||||||
contextualTweetRefBuilder = Some(contextualTweetRefBuilder)
|
|
||||||
))
|
|
||||||
|
|
||||||
private val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
|
|
||||||
def build(
|
|
||||||
organicCandidatePipelines: CandidateScope
|
|
||||||
): AdsDependentCandidatePipelineConfig[FollowingQuery] =
|
|
||||||
adsCandidatePipelineConfigBuilder.build[FollowingQuery](
|
|
||||||
adsCandidateSource = adsCandidateSource,
|
|
||||||
identifier = identifier,
|
|
||||||
adsDisplayLocationBuilder = query =>
|
|
||||||
if (query.params.getBoolean(EnableFastAds)) ads.DisplayLocation.TimelineHomeReverseChron
|
|
||||||
else ads.DisplayLocation.TimelineHome,
|
|
||||||
getOrganicItems = PipelineScopedOrganicItems(
|
|
||||||
pipelines = organicCandidatePipelines,
|
|
||||||
textFeature = TweetTextFeature,
|
|
||||||
languageFeature = TweetLanguageFeature
|
|
||||||
),
|
|
||||||
countNumOrganicItems = CountCandidatesFromPipelines(organicCandidatePipelines),
|
|
||||||
supportedClientParam = Some(EnableAdsCandidatePipelineParam),
|
|
||||||
gates = Seq(
|
|
||||||
ParamNotGate(
|
|
||||||
name = "AdsDisableInjectionBasedOnUserRole",
|
|
||||||
param = HomeGlobalParams.AdsDisableInjectionBasedOnUserRoleParam
|
|
||||||
),
|
|
||||||
ExcludeSoftUserGate,
|
|
||||||
NonEmptyCandidatesGate(organicCandidatePipelines)
|
|
||||||
),
|
|
||||||
filters = Seq(ValidAdImpressionIdFilter),
|
|
||||||
postFilterFeatureHydration = Seq(
|
|
||||||
ParamGatedCandidateFeatureHydrator(
|
|
||||||
EnableAdvertiserBrandSafetySettingsFeatureHydratorParam,
|
|
||||||
advertiserBrandSafetySettingsFeatureHydrator
|
|
||||||
)
|
|
||||||
),
|
|
||||||
decorator = Some(decorator),
|
|
||||||
alerts = alerts,
|
|
||||||
urtRequest = Some(true),
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,54 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.FollowingEarlybirdResponseFeatureTransformer
|
|
||||||
import com.twitter.home_mixer.functional_component.candidate_source.EarlybirdCandidateSource
|
|
||||||
import com.twitter.home_mixer.product.following.model.FollowingQuery
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.SGSFollowedUsersFeature
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptySeqFeatureGate
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.BaseCandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.Gate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineResultsTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.search.earlybird.{thriftscala => t}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class FollowingEarlybirdCandidatePipelineConfig @Inject() (
|
|
||||||
earlybirdCandidateSource: EarlybirdCandidateSource,
|
|
||||||
followingEarlybirdQueryTransformer: FollowingEarlybirdQueryTransformer)
|
|
||||||
extends CandidatePipelineConfig[
|
|
||||||
FollowingQuery,
|
|
||||||
t.EarlybirdRequest,
|
|
||||||
t.ThriftSearchResult,
|
|
||||||
TweetCandidate
|
|
||||||
] {
|
|
||||||
|
|
||||||
override val identifier: CandidatePipelineIdentifier =
|
|
||||||
CandidatePipelineIdentifier("FollowingEarlybird")
|
|
||||||
|
|
||||||
override val candidateSource: BaseCandidateSource[t.EarlybirdRequest, t.ThriftSearchResult] =
|
|
||||||
earlybirdCandidateSource
|
|
||||||
|
|
||||||
override val gates: Seq[Gate[FollowingQuery]] = Seq(
|
|
||||||
NonEmptySeqFeatureGate(SGSFollowedUsersFeature)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val queryTransformer: CandidatePipelineQueryTransformer[
|
|
||||||
FollowingQuery,
|
|
||||||
t.EarlybirdRequest
|
|
||||||
] = followingEarlybirdQueryTransformer
|
|
||||||
|
|
||||||
override val featuresFromCandidateSourceTransformers: Seq[
|
|
||||||
CandidateFeatureTransformer[t.ThriftSearchResult]
|
|
||||||
] = Seq(FollowingEarlybirdResponseFeatureTransformer)
|
|
||||||
|
|
||||||
override val resultTransformer: CandidatePipelineResultsTransformer[
|
|
||||||
t.ThriftSearchResult,
|
|
||||||
TweetCandidate
|
|
||||||
] = { sourceResult => TweetCandidate(id = sourceResult.id) }
|
|
||||||
}
|
|
Binary file not shown.
@ -1,84 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following
|
|
||||||
|
|
||||||
import com.twitter.finagle.thrift.ClientId
|
|
||||||
import com.twitter.finagle.tracing.Trace
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.RealGraphInNetworkScoresFeature
|
|
||||||
import com.twitter.home_mixer.product.following.model.FollowingQuery
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.ServerMaxResultsParam
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.SGSFollowedUsersFeature
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.operation.BottomCursor
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.operation.GapCursor
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.operation.TopCursor
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.MalformedCursor
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.PipelineFailure
|
|
||||||
import com.twitter.search.common.schema.earlybird.EarlybirdFieldConstants.EarlybirdFieldConstant
|
|
||||||
import com.twitter.search.earlybird.{thriftscala => t}
|
|
||||||
import com.twitter.search.queryparser.query.Conjunction
|
|
||||||
import com.twitter.search.queryparser.query.search.SearchOperator
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
import scala.jdk.CollectionConverters.asJavaIterableConverter
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
case class FollowingEarlybirdQueryTransformer @Inject() (clientId: ClientId)
|
|
||||||
extends CandidatePipelineQueryTransformer[FollowingQuery, t.EarlybirdRequest] {
|
|
||||||
|
|
||||||
override def transform(query: FollowingQuery): t.EarlybirdRequest = {
|
|
||||||
val followedUserIds =
|
|
||||||
query.features.map(_.get(SGSFollowedUsersFeature)).getOrElse(Seq.empty).toSet
|
|
||||||
val realGraphInNetworkFollowedUserIds =
|
|
||||||
query.features.map(_.get(RealGraphInNetworkScoresFeature)).getOrElse(Map.empty).keySet
|
|
||||||
val userId = query.getRequiredUserId
|
|
||||||
val combinedUserIds = userId +: followedUserIds.toSeq
|
|
||||||
|
|
||||||
val baseFollowedUsersSearchOperator = new SearchOperator.Builder()
|
|
||||||
.setType(SearchOperator.Type.FEATURE_VALUE_IN_ACCEPT_LIST_OR_UNSET)
|
|
||||||
.addOperand(EarlybirdFieldConstant.DIRECTED_AT_USER_ID_CSF.getFieldName)
|
|
||||||
|
|
||||||
val followedUsersQuery =
|
|
||||||
baseFollowedUsersSearchOperator.addOperands(combinedUserIds.map(_.toString).asJava).build()
|
|
||||||
|
|
||||||
val searchQuery = query.pipelineCursor
|
|
||||||
.map { cursor =>
|
|
||||||
val sinceIdQuery =
|
|
||||||
(id: Long) => new SearchOperator(SearchOperator.Type.SINCE_ID, id.toString)
|
|
||||||
val maxIdQuery = // max ID is inclusive, so subtract 1
|
|
||||||
(id: Long) => new SearchOperator(SearchOperator.Type.MAX_ID, (id - 1).toString)
|
|
||||||
|
|
||||||
(cursor.cursorType, cursor.id, cursor.gapBoundaryId) match {
|
|
||||||
case (Some(TopCursor), Some(sinceId), _) =>
|
|
||||||
new Conjunction(sinceIdQuery(sinceId), followedUsersQuery)
|
|
||||||
case (Some(BottomCursor), Some(maxId), _) =>
|
|
||||||
new Conjunction(maxIdQuery(maxId), followedUsersQuery)
|
|
||||||
case (Some(GapCursor), Some(maxId), Some(sinceId)) =>
|
|
||||||
new Conjunction(sinceIdQuery(sinceId), maxIdQuery(maxId), followedUsersQuery)
|
|
||||||
case (Some(GapCursor), _, _) =>
|
|
||||||
throw PipelineFailure(MalformedCursor, "Invalid cursor " + cursor.toString)
|
|
||||||
case _ => followedUsersQuery
|
|
||||||
}
|
|
||||||
}.getOrElse(followedUsersQuery)
|
|
||||||
|
|
||||||
val metadataOptions = t.ThriftSearchResultMetadataOptions(
|
|
||||||
getInReplyToStatusId = true,
|
|
||||||
getReferencedTweetAuthorId = true,
|
|
||||||
getFromUserId = true
|
|
||||||
)
|
|
||||||
|
|
||||||
t.EarlybirdRequest(
|
|
||||||
searchQuery = t.ThriftSearchQuery(
|
|
||||||
serializedQuery = Some(searchQuery.serialize),
|
|
||||||
fromUserIDFilter64 = Some(combinedUserIds),
|
|
||||||
numResults = query.requestedMaxResults.getOrElse(query.params(ServerMaxResultsParam)),
|
|
||||||
rankingMode = t.ThriftSearchRankingMode.Recency,
|
|
||||||
resultMetadataOptions = Some(metadataOptions),
|
|
||||||
searcherId = query.getOptionalUserId,
|
|
||||||
),
|
|
||||||
getOlderResults = Some(true), // needed for archive access to older tweets
|
|
||||||
clientRequestID = Some(s"${Trace.id.traceId}"),
|
|
||||||
followedUserIds = Some(combinedUserIds),
|
|
||||||
numResultsToReturnAtRoot = Some(query.params(ServerMaxResultsParam)),
|
|
||||||
clientId = Some(clientId.name),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,38 +0,0 @@
|
|||||||
package com.twitter.home_mixer.candidate_pipeline
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.InReplyToTweetIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsRetweetFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.SourceTweetIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.SourceUserIdFeature
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.TransformerIdentifier
|
|
||||||
import com.twitter.search.earlybird.{thriftscala => t}
|
|
||||||
|
|
||||||
object FollowingEarlybirdResponseFeatureTransformer
|
|
||||||
extends CandidateFeatureTransformer[t.ThriftSearchResult] {
|
|
||||||
|
|
||||||
override val identifier: TransformerIdentifier =
|
|
||||||
TransformerIdentifier("FollowingEarlybirdResponse")
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] = Set(
|
|
||||||
AuthorIdFeature,
|
|
||||||
InReplyToTweetIdFeature,
|
|
||||||
IsRetweetFeature,
|
|
||||||
SourceTweetIdFeature,
|
|
||||||
SourceUserIdFeature,
|
|
||||||
)
|
|
||||||
|
|
||||||
override def transform(candidate: t.ThriftSearchResult): FeatureMap = FeatureMapBuilder()
|
|
||||||
.add(AuthorIdFeature, candidate.tweetypieTweet.flatMap(_.coreData.map(_.userId)))
|
|
||||||
.add(
|
|
||||||
InReplyToTweetIdFeature,
|
|
||||||
candidate.tweetypieTweet.flatMap(_.coreData.flatMap(_.reply.flatMap(_.inReplyToStatusId))))
|
|
||||||
.add(IsRetweetFeature, candidate.metadata.exists(_.isRetweet.contains(true)))
|
|
||||||
.add(SourceTweetIdFeature, candidate.sourceTweetypieTweet.map(_.id))
|
|
||||||
.add(SourceUserIdFeature, candidate.sourceTweetypieTweet.flatMap(_.coreData.map(_.userId)))
|
|
||||||
.build()
|
|
||||||
}
|
|
Binary file not shown.
@ -1,299 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following
|
|
||||||
|
|
||||||
import com.twitter.clientapp.{thriftscala => ca}
|
|
||||||
import com.twitter.goldfinch.api.AdsInjectionSurfaceAreas
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.ConversationServiceCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.EditedTweetsCandidatePipelineConfig
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.NewTweetsPillCandidatePipelineConfig
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.HomeConversationServiceCandidateDecorator
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.AddEntriesWithReplaceAndShowAlertAndCoverInstructionBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator._
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateHomeClientEventDetails
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateNewTweetsPillDecoration
|
|
||||||
import com.twitter.home_mixer.functional_component.side_effect._
|
|
||||||
import com.twitter.home_mixer.model.GapIncludeInstruction
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.EnableImpressionBloomFilter
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.MaxNumberReplaceInstructionsParam
|
|
||||||
import com.twitter.home_mixer.param.HomeMixerFlagName.ScribeClientEventsFlag
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.SGSFollowedUsersQueryFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.following.model.FollowingQuery
|
|
||||||
import com.twitter.home_mixer.product.following.model.HomeMixerExternalStrings
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.EnableFlipInjectionModuleCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.FlipInlineInjectionModulePosition
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.ServerMaxResultsParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.WhoToFollowPositionParam
|
|
||||||
import com.twitter.home_mixer.util.CandidatesUtil
|
|
||||||
import com.twitter.inject.annotations.Flag
|
|
||||||
import com.twitter.logpipeline.client.common.EventPublisher
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.async.AsyncQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.impressed_tweets.ImpressedTweetsQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.param_gated.AsyncParamGatedQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptyCandidatesGate
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.flexible_injection_pipeline.FlipPromptDependentCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.UrtDomainMarshaller
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedBottomCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedGapCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedTopCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ReplaceAllEntries
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ReplaceEntryInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ShowAlertInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ShowCoverInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.StaticTimelineScribeConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.UrtMetadataBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropMaxCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropMaxModuleItemCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropModuleTooFewModuleItemResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertAppendResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertFixedPositionResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.SelectConditionally
|
|
||||||
import com.twitter.product_mixer.component_library.selector.UpdateSortCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.ads.AdsInjector
|
|
||||||
import com.twitter.product_mixer.component_library.selector.ads.InsertAdResults
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipeline
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipelines
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.StaticParam
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.QueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.TransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.response.urt.UrtTransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.premarshaller.DomainMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.selector.Selector
|
|
||||||
import com.twitter.product_mixer.core.functional_component.side_effect.PipelineResultSideEffect
|
|
||||||
import com.twitter.product_mixer.core.model.common.UniversalNoun
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.MixerPipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.Timeline
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineModule
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineScribeConfig
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.item.tweet.TweetItem
|
|
||||||
import com.twitter.product_mixer.core.pipeline.FailOpenPolicy
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.DependentCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.mixer.MixerPipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.product.guice.scope.ProductScoped
|
|
||||||
import com.twitter.stringcenter.client.StringCenter
|
|
||||||
import com.twitter.timelines.render.{thriftscala => urt}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Provider
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class FollowingMixerPipelineConfig @Inject() (
|
|
||||||
followingEarlybirdCandidatePipelineConfig: FollowingEarlybirdCandidatePipelineConfig,
|
|
||||||
conversationServiceCandidatePipelineConfigBuilder: ConversationServiceCandidatePipelineConfigBuilder[
|
|
||||||
FollowingQuery
|
|
||||||
],
|
|
||||||
homeFeedbackActionInfoBuilder: HomeFeedbackActionInfoBuilder,
|
|
||||||
followingAdsCandidatePipelineBuilder: FollowingAdsCandidatePipelineBuilder,
|
|
||||||
followingWhoToFollowCandidatePipelineConfigBuilder: FollowingWhoToFollowCandidatePipelineConfigBuilder,
|
|
||||||
flipPromptDependentCandidatePipelineConfigBuilder: FlipPromptDependentCandidatePipelineConfigBuilder,
|
|
||||||
editedTweetsCandidatePipelineConfig: EditedTweetsCandidatePipelineConfig,
|
|
||||||
newTweetsPillCandidatePipelineConfig: NewTweetsPillCandidatePipelineConfig[FollowingQuery],
|
|
||||||
dismissInfoQueryFeatureHydrator: DismissInfoQueryFeatureHydrator,
|
|
||||||
gizmoduckUserQueryFeatureHydrator: GizmoduckUserQueryFeatureHydrator,
|
|
||||||
persistenceStoreQueryFeatureHydrator: PersistenceStoreQueryFeatureHydrator,
|
|
||||||
realGraphInNetworkSourceQueryHydrator: RealGraphInNetworkScoresQueryFeatureHydrator,
|
|
||||||
requestQueryFeatureHydrator: RequestQueryFeatureHydrator[FollowingQuery],
|
|
||||||
sgsFollowedUsersQueryFeatureHydrator: SGSFollowedUsersQueryFeatureHydrator,
|
|
||||||
impressionBloomFilterQueryFeatureHydrator: ImpressionBloomFilterQueryFeatureHydrator[
|
|
||||||
FollowingQuery
|
|
||||||
],
|
|
||||||
manhattanTweetImpressionsQueryFeatureHydrator: TweetImpressionsQueryFeatureHydrator[
|
|
||||||
FollowingQuery
|
|
||||||
],
|
|
||||||
memcacheTweetImpressionsQueryFeatureHydrator: ImpressedTweetsQueryFeatureHydrator,
|
|
||||||
lastNonPollingTimeQueryFeatureHydrator: LastNonPollingTimeQueryFeatureHydrator,
|
|
||||||
adsInjector: AdsInjector,
|
|
||||||
updateLastNonPollingTimeSideEffect: UpdateLastNonPollingTimeSideEffect[FollowingQuery, Timeline],
|
|
||||||
publishClientSentImpressionsEventBusSideEffect: PublishClientSentImpressionsEventBusSideEffect,
|
|
||||||
publishClientSentImpressionsManhattanSideEffect: PublishClientSentImpressionsManhattanSideEffect,
|
|
||||||
publishImpressionBloomFilterSideEffect: PublishImpressionBloomFilterSideEffect,
|
|
||||||
updateTimelinesPersistenceStoreSideEffect: UpdateTimelinesPersistenceStoreSideEffect,
|
|
||||||
truncateTimelinesPersistenceStoreSideEffect: TruncateTimelinesPersistenceStoreSideEffect,
|
|
||||||
homeTimelineServedCandidatesSideEffect: HomeScribeServedCandidatesSideEffect,
|
|
||||||
clientEventsScribeEventPublisher: EventPublisher[ca.LogEvent],
|
|
||||||
externalStrings: HomeMixerExternalStrings,
|
|
||||||
@ProductScoped stringCenterProvider: Provider[StringCenter],
|
|
||||||
urtTransportMarshaller: UrtTransportMarshaller,
|
|
||||||
@Flag(ScribeClientEventsFlag) enableScribeClientEvents: Boolean)
|
|
||||||
extends MixerPipelineConfig[FollowingQuery, Timeline, urt.TimelineResponse] {
|
|
||||||
|
|
||||||
override val identifier: MixerPipelineIdentifier = MixerPipelineIdentifier("Following")
|
|
||||||
|
|
||||||
private val dependentCandidatesStep = MixerPipelineConfig.dependentCandidatePipelinesStep
|
|
||||||
private val resultSelectorsStep = MixerPipelineConfig.resultSelectorsStep
|
|
||||||
|
|
||||||
override val fetchQueryFeatures: Seq[QueryFeatureHydrator[FollowingQuery]] = Seq(
|
|
||||||
requestQueryFeatureHydrator,
|
|
||||||
sgsFollowedUsersQueryFeatureHydrator,
|
|
||||||
realGraphInNetworkSourceQueryHydrator,
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, dismissInfoQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, gizmoduckUserQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, persistenceStoreQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, lastNonPollingTimeQueryFeatureHydrator),
|
|
||||||
AsyncParamGatedQueryFeatureHydrator(
|
|
||||||
EnableImpressionBloomFilter,
|
|
||||||
resultSelectorsStep,
|
|
||||||
impressionBloomFilterQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(resultSelectorsStep, manhattanTweetImpressionsQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(resultSelectorsStep, memcacheTweetImpressionsQueryFeatureHydrator)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val earlybirdCandidatePipelineScope =
|
|
||||||
SpecificPipeline(followingEarlybirdCandidatePipelineConfig.identifier)
|
|
||||||
|
|
||||||
private val conversationServiceCandidatePipelineConfig =
|
|
||||||
conversationServiceCandidatePipelineConfigBuilder.build(
|
|
||||||
Seq(NonEmptyCandidatesGate(earlybirdCandidatePipelineScope)),
|
|
||||||
HomeConversationServiceCandidateDecorator(homeFeedbackActionInfoBuilder)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val followingAdsCandidatePipelineConfig =
|
|
||||||
followingAdsCandidatePipelineBuilder.build(earlybirdCandidatePipelineScope)
|
|
||||||
|
|
||||||
private val followingWhoToFollowCandidatePipelineConfig =
|
|
||||||
followingWhoToFollowCandidatePipelineConfigBuilder.build(earlybirdCandidatePipelineScope)
|
|
||||||
|
|
||||||
private val flipPromptCandidatePipelineConfig =
|
|
||||||
flipPromptDependentCandidatePipelineConfigBuilder.build[FollowingQuery](
|
|
||||||
supportedClientParam = Some(EnableFlipInjectionModuleCandidatePipelineParam)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val candidatePipelines: Seq[CandidatePipelineConfig[FollowingQuery, _, _, _]] =
|
|
||||||
Seq(followingEarlybirdCandidatePipelineConfig)
|
|
||||||
|
|
||||||
override val dependentCandidatePipelines: Seq[
|
|
||||||
DependentCandidatePipelineConfig[FollowingQuery, _, _, _]
|
|
||||||
] = Seq(
|
|
||||||
conversationServiceCandidatePipelineConfig,
|
|
||||||
followingAdsCandidatePipelineConfig,
|
|
||||||
followingWhoToFollowCandidatePipelineConfig,
|
|
||||||
flipPromptCandidatePipelineConfig,
|
|
||||||
editedTweetsCandidatePipelineConfig,
|
|
||||||
newTweetsPillCandidatePipelineConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
override val failOpenPolicies: Map[CandidatePipelineIdentifier, FailOpenPolicy] = Map(
|
|
||||||
followingAdsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
followingWhoToFollowCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
flipPromptCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
editedTweetsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
newTweetsPillCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val resultSelectors: Seq[Selector[FollowingQuery]] = Seq(
|
|
||||||
UpdateSortCandidates(
|
|
||||||
ordering = CandidatesUtil.reverseChronTweetsOrdering,
|
|
||||||
candidatePipeline = conversationServiceCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = editedTweetsCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = MaxNumberReplaceInstructionsParam
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = conversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = ServerMaxResultsParam
|
|
||||||
),
|
|
||||||
DropModuleTooFewModuleItemResults(
|
|
||||||
candidatePipeline = followingWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
minModuleItemsParam = StaticParam(WhoToFollowArmCandidatePipelineConfig.MinCandidatesSize)
|
|
||||||
),
|
|
||||||
DropMaxModuleItemCandidates(
|
|
||||||
candidatePipeline = followingWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
maxModuleItemsParam = StaticParam(WhoToFollowArmCandidatePipelineConfig.MaxCandidatesSize)
|
|
||||||
),
|
|
||||||
InsertAppendResults(candidatePipeline = conversationServiceCandidatePipelineConfig.identifier),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = followingWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = WhoToFollowPositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = flipPromptCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = FlipInlineInjectionModulePosition
|
|
||||||
),
|
|
||||||
InsertAdResults(
|
|
||||||
surfaceAreaName = AdsInjectionSurfaceAreas.HomeTimeline,
|
|
||||||
adsInjector = adsInjector.forSurfaceArea(AdsInjectionSurfaceAreas.HomeTimeline),
|
|
||||||
adsCandidatePipeline = followingAdsCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
// This selector must come after the tweets are inserted into the results
|
|
||||||
UpdateNewTweetsPillDecoration(
|
|
||||||
pipelineScope = SpecificPipelines(
|
|
||||||
conversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
newTweetsPillCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
stringCenter = stringCenterProvider.get(),
|
|
||||||
seeNewTweetsString = externalStrings.seeNewTweetsString,
|
|
||||||
tweetedString = externalStrings.tweetedString
|
|
||||||
),
|
|
||||||
InsertAppendResults(candidatePipeline = editedTweetsCandidatePipelineConfig.identifier),
|
|
||||||
SelectConditionally(
|
|
||||||
selector =
|
|
||||||
InsertAppendResults(candidatePipeline = newTweetsPillCandidatePipelineConfig.identifier),
|
|
||||||
includeSelector = (_, _, results) => CandidatesUtil.containsType[TweetCandidate](results)
|
|
||||||
),
|
|
||||||
UpdateHomeClientEventDetails(
|
|
||||||
candidatePipelines = Set(conversationServiceCandidatePipelineConfig.identifier)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val homeScribeClientEventSideEffect = HomeScribeClientEventSideEffect(
|
|
||||||
enableScribeClientEvents = enableScribeClientEvents,
|
|
||||||
logPipelinePublisher = clientEventsScribeEventPublisher,
|
|
||||||
injectedTweetsCandidatePipelineIdentifiers =
|
|
||||||
Seq(conversationServiceCandidatePipelineConfig.identifier),
|
|
||||||
adsCandidatePipelineIdentifier = Some(followingAdsCandidatePipelineConfig.identifier),
|
|
||||||
whoToFollowCandidatePipelineIdentifier =
|
|
||||||
Some(followingWhoToFollowCandidatePipelineConfig.identifier),
|
|
||||||
)
|
|
||||||
|
|
||||||
override val resultSideEffects: Seq[PipelineResultSideEffect[FollowingQuery, Timeline]] = Seq(
|
|
||||||
homeScribeClientEventSideEffect,
|
|
||||||
homeTimelineServedCandidatesSideEffect,
|
|
||||||
publishClientSentImpressionsEventBusSideEffect,
|
|
||||||
publishClientSentImpressionsManhattanSideEffect,
|
|
||||||
publishImpressionBloomFilterSideEffect,
|
|
||||||
truncateTimelinesPersistenceStoreSideEffect,
|
|
||||||
updateLastNonPollingTimeSideEffect,
|
|
||||||
updateTimelinesPersistenceStoreSideEffect,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val domainMarshaller: DomainMarshaller[FollowingQuery, Timeline] = {
|
|
||||||
val instructionBuilders = Seq(
|
|
||||||
ReplaceEntryInstructionBuilder(ReplaceAllEntries),
|
|
||||||
AddEntriesWithReplaceAndShowAlertAndCoverInstructionBuilder(),
|
|
||||||
ShowAlertInstructionBuilder(),
|
|
||||||
ShowCoverInstructionBuilder(),
|
|
||||||
)
|
|
||||||
|
|
||||||
val idSelector: PartialFunction[UniversalNoun[_], Long] = {
|
|
||||||
// exclude ads while determining tweet cursor values
|
|
||||||
case item: TweetItem if item.promotedMetadata.isEmpty => item.id
|
|
||||||
case module: TimelineModule
|
|
||||||
if module.items.headOption.exists(_.item.isInstanceOf[TweetItem]) =>
|
|
||||||
module.items.last.item match { case item: TweetItem => item.id }
|
|
||||||
}
|
|
||||||
val topCursorBuilder = OrderedTopCursorBuilder(idSelector)
|
|
||||||
val bottomCursorBuilder =
|
|
||||||
OrderedBottomCursorBuilder(idSelector, GapIncludeInstruction.inverse())
|
|
||||||
val gapCursorBuilder = OrderedGapCursorBuilder(idSelector, GapIncludeInstruction)
|
|
||||||
|
|
||||||
val metadataBuilder = UrtMetadataBuilder(
|
|
||||||
title = None,
|
|
||||||
scribeConfigBuilder = Some(
|
|
||||||
StaticTimelineScribeConfigBuilder(
|
|
||||||
TimelineScribeConfig(page = Some("following"), section = None, entityToken = None)))
|
|
||||||
)
|
|
||||||
|
|
||||||
UrtDomainMarshaller(
|
|
||||||
instructionBuilders = instructionBuilders,
|
|
||||||
metadataBuilder = Some(metadataBuilder),
|
|
||||||
cursorBuilders = Seq(topCursorBuilder, bottomCursorBuilder, gapCursorBuilder)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val transportMarshaller: TransportMarshaller[Timeline, urt.TimelineResponse] =
|
|
||||||
urtTransportMarshaller
|
|
||||||
}
|
|
Binary file not shown.
@ -1,131 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following
|
|
||||||
|
|
||||||
import com.twitter.conversions.DurationOps._
|
|
||||||
import com.twitter.home_mixer.marshaller.timelines.ChronologicalCursorUnmarshaller
|
|
||||||
import com.twitter.home_mixer.model.request.FollowingProduct
|
|
||||||
import com.twitter.home_mixer.model.request.FollowingProductContext
|
|
||||||
import com.twitter.home_mixer.model.request.HomeMixerRequest
|
|
||||||
import com.twitter.home_mixer.product.following.model.FollowingQuery
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.ServerMaxResultsParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParamConfig
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAccessPolicy.DefaultHomeMixerAccessPolicy
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig.DefaultNotificationGroup
|
|
||||||
import com.twitter.product_mixer.component_library.model.cursor.UrtOrderedCursor
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.cursor.UrtCursorSerializer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.access_policy.AccessPolicy
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.Alert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.EmptyResponseRateAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.LatencyAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.P99
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.SuccessRateAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.ThroughputAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.predicate.TriggerIfAbove
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.predicate.TriggerIfBelow
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.predicate.TriggerIfLatencyAbove
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.ComponentIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.ProductPipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.request.Product
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.operation.GapCursor
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.operation.TopCursor
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.BadRequest
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.MalformedCursor
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.PipelineFailure
|
|
||||||
import com.twitter.product_mixer.core.pipeline.product.ProductPipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.product.ProductParamConfig
|
|
||||||
import com.twitter.product_mixer.core.util.SortIndexBuilder
|
|
||||||
import com.twitter.timelines.configapi.Params
|
|
||||||
import com.twitter.timelines.render.{thriftscala => urt}
|
|
||||||
import com.twitter.timelines.util.RequestCursorSerializer
|
|
||||||
import com.twitter.util.Time
|
|
||||||
import com.twitter.util.Try
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class FollowingProductPipelineConfig @Inject() (
|
|
||||||
followingMixerPipelineConfig: FollowingMixerPipelineConfig,
|
|
||||||
followingParamConfig: FollowingParamConfig)
|
|
||||||
extends ProductPipelineConfig[HomeMixerRequest, FollowingQuery, urt.TimelineResponse] {
|
|
||||||
|
|
||||||
override val identifier: ProductPipelineIdentifier = ProductPipelineIdentifier("Following")
|
|
||||||
|
|
||||||
override val product: Product = FollowingProduct
|
|
||||||
override val paramConfig: ProductParamConfig = followingParamConfig
|
|
||||||
|
|
||||||
override def pipelineQueryTransformer(
|
|
||||||
request: HomeMixerRequest,
|
|
||||||
params: Params
|
|
||||||
): FollowingQuery = {
|
|
||||||
val context = request.productContext match {
|
|
||||||
case Some(context: FollowingProductContext) => context
|
|
||||||
case _ => throw PipelineFailure(BadRequest, "FollowingProductContext not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
val debugOptions = request.debugParams.flatMap(_.debugOptions)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlike other clients, newly created tweets on Android have the sort index set to the current
|
|
||||||
* time instead of the top sort index + 1, so these tweets get stuck at the top of the timeline
|
|
||||||
* if subsequent timeline responses use the sort index from the previous response instead of
|
|
||||||
* the current time.
|
|
||||||
*/
|
|
||||||
val pipelineCursor = request.serializedRequestCursor.flatMap { cursor =>
|
|
||||||
Try(UrtCursorSerializer.deserializeOrderedCursor(cursor))
|
|
||||||
.getOrElse(ChronologicalCursorUnmarshaller(RequestCursorSerializer.deserialize(cursor)))
|
|
||||||
.map {
|
|
||||||
case UrtOrderedCursor(_, id, Some(GapCursor), gapBoundaryId)
|
|
||||||
if id.isEmpty || gapBoundaryId.isEmpty =>
|
|
||||||
throw PipelineFailure(MalformedCursor, "Gap Cursor bounds not defined")
|
|
||||||
case topCursor @ UrtOrderedCursor(_, _, Some(TopCursor), _) =>
|
|
||||||
val queryTime = debugOptions.flatMap(_.requestTimeOverride).getOrElse(Time.now)
|
|
||||||
topCursor.copy(initialSortIndex = SortIndexBuilder.timeToId(queryTime))
|
|
||||||
case cursor => cursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FollowingQuery(
|
|
||||||
params = params,
|
|
||||||
clientContext = request.clientContext,
|
|
||||||
features = None,
|
|
||||||
pipelineCursor = pipelineCursor,
|
|
||||||
requestedMaxResults = Some(params(ServerMaxResultsParam)),
|
|
||||||
debugOptions = debugOptions,
|
|
||||||
deviceContext = context.deviceContext,
|
|
||||||
seenTweetIds = context.seenTweetIds,
|
|
||||||
dspClientContext = context.dspClientContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val pipelines: Seq[PipelineConfig] = Seq(followingMixerPipelineConfig)
|
|
||||||
|
|
||||||
override def pipelineSelector(
|
|
||||||
query: FollowingQuery
|
|
||||||
): ComponentIdentifier = followingMixerPipelineConfig.identifier
|
|
||||||
|
|
||||||
override val alerts: Seq[Alert] = Seq(
|
|
||||||
SuccessRateAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
warnPredicate = TriggerIfBelow(99.9, 20, 30),
|
|
||||||
criticalPredicate = TriggerIfBelow(99.9, 30, 30),
|
|
||||||
),
|
|
||||||
LatencyAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
percentile = P99,
|
|
||||||
warnPredicate = TriggerIfLatencyAbove(1100.millis, 15, 30),
|
|
||||||
criticalPredicate = TriggerIfLatencyAbove(1200.millis, 15, 30)
|
|
||||||
),
|
|
||||||
ThroughputAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
warnPredicate = TriggerIfAbove(18000),
|
|
||||||
criticalPredicate = TriggerIfAbove(20000)
|
|
||||||
),
|
|
||||||
EmptyResponseRateAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
warnPredicate = TriggerIfAbove(65),
|
|
||||||
criticalPredicate = TriggerIfAbove(80)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val debugAccessPolicies: Set[AccessPolicy] = DefaultHomeMixerAccessPolicy
|
|
||||||
}
|
|
Binary file not shown.
@ -1,65 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeWhoToFollowFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.DismissFatigueGate
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.TimelinesPersistenceStoreLastInjectionGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.DismissInfoFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.PersistenceEntriesFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.WhoToFollowExcludedUserIdsFeature
|
|
||||||
import com.twitter.home_mixer.product.following.model.FollowingQuery
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.EnableWhoToFollowCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.WhoToFollowDisplayLocationParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.WhoToFollowDisplayTypeIdParam
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam.WhoToFollowMinInjectionIntervalParam
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.ParamWhoToFollowModuleDisplayTypeBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptyCandidatesGate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmDependentCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmDependentCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.CandidateScope
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.StaticParam
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.BaseGate
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.timelineservice.model.rich.EntityIdType
|
|
||||||
import com.twitter.timelineservice.suggests.thriftscala.SuggestType
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class FollowingWhoToFollowCandidatePipelineConfigBuilder @Inject() (
|
|
||||||
whoToFollowArmDependentCandidatePipelineConfigBuilder: WhoToFollowArmDependentCandidatePipelineConfigBuilder,
|
|
||||||
homeWhoToFollowFeedbackActionInfoBuilder: HomeWhoToFollowFeedbackActionInfoBuilder) {
|
|
||||||
|
|
||||||
def build(
|
|
||||||
requiredNonEmptyPipelines: CandidateScope
|
|
||||||
): WhoToFollowArmDependentCandidatePipelineConfig[FollowingQuery] = {
|
|
||||||
val gates: Seq[BaseGate[PipelineQuery]] = Seq(
|
|
||||||
TimelinesPersistenceStoreLastInjectionGate(
|
|
||||||
WhoToFollowMinInjectionIntervalParam,
|
|
||||||
PersistenceEntriesFeature,
|
|
||||||
EntityIdType.WhoToFollow
|
|
||||||
),
|
|
||||||
DismissFatigueGate(SuggestType.WhoToFollow, DismissInfoFeature),
|
|
||||||
NonEmptyCandidatesGate(requiredNonEmptyPipelines)
|
|
||||||
)
|
|
||||||
|
|
||||||
whoToFollowArmDependentCandidatePipelineConfigBuilder.build[FollowingQuery](
|
|
||||||
identifier = WhoToFollowArmCandidatePipelineConfig.identifier,
|
|
||||||
supportedClientParam = Some(EnableWhoToFollowCandidatePipelineParam),
|
|
||||||
alerts = alerts,
|
|
||||||
gates = gates,
|
|
||||||
moduleDisplayTypeBuilder =
|
|
||||||
ParamWhoToFollowModuleDisplayTypeBuilder(WhoToFollowDisplayTypeIdParam),
|
|
||||||
feedbackActionInfoBuilder = Some(homeWhoToFollowFeedbackActionInfoBuilder),
|
|
||||||
displayLocationParam = StaticParam(WhoToFollowDisplayLocationParam.default),
|
|
||||||
excludedUserIdsFeature = Some(WhoToFollowExcludedUserIdsFeature),
|
|
||||||
profileUserIdFeature = None
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(70),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model/request",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following/param",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/cursor",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/query/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/flexible_injection_pipeline",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline",
|
|
||||||
"stringcenter/client",
|
|
||||||
"stringcenter/client/src/main/java",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/cursor",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/query/ads",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,51 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following.model
|
|
||||||
|
|
||||||
import com.twitter.adserver.thriftscala.HomeTimelineType
|
|
||||||
import com.twitter.adserver.thriftscala.TimelineRequestParams
|
|
||||||
import com.twitter.home_mixer.model.HomeAdsQuery
|
|
||||||
import com.twitter.dspbidder.commons.{thriftscala => dsp}
|
|
||||||
import com.twitter.home_mixer.model.request.DeviceContext
|
|
||||||
import com.twitter.home_mixer.model.request.HasDeviceContext
|
|
||||||
import com.twitter.home_mixer.model.request.HasSeenTweetIds
|
|
||||||
import com.twitter.home_mixer.model.request.FollowingProduct
|
|
||||||
import com.twitter.onboarding.task.service.{thriftscala => ots}
|
|
||||||
import com.twitter.product_mixer.component_library.model.cursor.UrtOrderedCursor
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.flexible_injection_pipeline.transformer.HasFlipInjectionParams
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.request._
|
|
||||||
import com.twitter.product_mixer.core.pipeline.HasPipelineCursor
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.timelines.configapi.Params
|
|
||||||
|
|
||||||
case class FollowingQuery(
|
|
||||||
override val params: Params,
|
|
||||||
override val clientContext: ClientContext,
|
|
||||||
override val pipelineCursor: Option[UrtOrderedCursor],
|
|
||||||
override val requestedMaxResults: Option[Int],
|
|
||||||
override val debugOptions: Option[DebugOptions],
|
|
||||||
override val features: Option[FeatureMap],
|
|
||||||
override val deviceContext: Option[DeviceContext],
|
|
||||||
override val seenTweetIds: Option[Seq[Long]],
|
|
||||||
override val dspClientContext: Option[dsp.DspClientContext])
|
|
||||||
extends PipelineQuery
|
|
||||||
with HasPipelineCursor[UrtOrderedCursor]
|
|
||||||
with HasDeviceContext
|
|
||||||
with HasSeenTweetIds
|
|
||||||
with HasFlipInjectionParams
|
|
||||||
with HomeAdsQuery {
|
|
||||||
override val product: Product = FollowingProduct
|
|
||||||
|
|
||||||
override def withFeatureMap(features: FeatureMap): FollowingQuery =
|
|
||||||
copy(features = Some(features))
|
|
||||||
|
|
||||||
override val timelineRequestParams: Option[TimelineRequestParams] =
|
|
||||||
Some(TimelineRequestParams(homeTimelineType = Some(HomeTimelineType.HomeLatest)))
|
|
||||||
|
|
||||||
// Fields below are used for FLIP Injection in Onboarding Task Service (OTS)
|
|
||||||
override val displayLocation: ots.DisplayLocation = ots.DisplayLocation.HomeLatestTimeline
|
|
||||||
override val rankingDisablerWithLatestControlsAvailable: Option[Boolean] = None
|
|
||||||
override val isEmptyState: Option[Boolean] = None
|
|
||||||
override val isFirstRequestAfterSignup: Option[Boolean] = None
|
|
||||||
override val isEndOfTimeline: Option[Boolean] = None
|
|
||||||
override val timelineId: Option[Long] = None
|
|
||||||
}
|
|
Binary file not shown.
@ -1,87 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following.model
|
|
||||||
|
|
||||||
import com.twitter.product_mixer.core.product.guice.scope.ProductScoped
|
|
||||||
import com.twitter.stringcenter.client.ExternalStringRegistry
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Provider
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class HomeMixerExternalStrings @Inject() (
|
|
||||||
@ProductScoped externalStringRegistryProvider: Provider[ExternalStringRegistry]) {
|
|
||||||
val seeNewTweetsString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SeeNewTweets")
|
|
||||||
val tweetedString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Tweeted")
|
|
||||||
val muteUserString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.muteUser")
|
|
||||||
val blockUserString = externalStringRegistryProvider.get().createProdString("Feedback.blockUser")
|
|
||||||
val unfollowUserString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.unfollowUser")
|
|
||||||
val unfollowUserConfirmationString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.unfollowUserConfirmation")
|
|
||||||
val reportTweetString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.reportTweet")
|
|
||||||
val dontLikeString = externalStringRegistryProvider.get().createProdString("Feedback.dontLike")
|
|
||||||
val dontLikeConfirmationString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.dontLikeConfirmation")
|
|
||||||
val showFewerTweetsString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.showFewerTweets")
|
|
||||||
val showFewerTweetsConfirmationString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.showFewerTweetsConfirmation")
|
|
||||||
val showFewerRetweetsString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.showFewerRetweets")
|
|
||||||
val showFewerRetweetsConfirmationString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.showFewerRetweetsConfirmation")
|
|
||||||
val notRelevantString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.notRelevant")
|
|
||||||
val notRelevantConfirmationString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("Feedback.notRelevantConfirmation")
|
|
||||||
|
|
||||||
val socialContextOneUserLikedString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.oneUserLiked")
|
|
||||||
val socialContextTwoUsersLikedString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.twoUsersLiked")
|
|
||||||
val socialContextMoreUsersLikedString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.moreUsersLiked")
|
|
||||||
val socialContextLikedByTimelineTitle =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.likedByTimelineTitle")
|
|
||||||
|
|
||||||
val socialContextOneUserFollowsString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.oneUserFollows")
|
|
||||||
val socialContextTwoUsersFollowString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.twoUsersFollow")
|
|
||||||
val socialContextMoreUsersFollowString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.moreUsersFollow")
|
|
||||||
val socialContextFollowedByTimelineTitle =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.followedByTimelineTitle")
|
|
||||||
|
|
||||||
val socialContextYouMightLikeString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.youMightLike")
|
|
||||||
|
|
||||||
val socialContextExtendedReply =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.extendedReply")
|
|
||||||
val socialContextReceivedReply =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.receivedReply")
|
|
||||||
|
|
||||||
val socialContextPopularVideoString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("SocialContext.popularVideo")
|
|
||||||
|
|
||||||
val socialContextPopularInYourAreaString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("PopgeoTweet.socialProof")
|
|
||||||
|
|
||||||
val listToFollowModuleHeaderString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("ListToFollowModule.header")
|
|
||||||
val listToFollowModuleFooterString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("ListToFollowModule.footer")
|
|
||||||
val pinnedListsModuleHeaderString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("PinnedListModule.header")
|
|
||||||
val pinnedListsModuleEmptyStateMessageString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("PinnedListModule.emptyStateMessage")
|
|
||||||
|
|
||||||
val ownedSubscribedListsModuleHeaderString =
|
|
||||||
externalStringRegistryProvider.get().createProdString("OwnedSubscribedListModule.header")
|
|
||||||
val ownedSubscribedListsModuleEmptyStateMessageString =
|
|
||||||
externalStringRegistryProvider
|
|
||||||
.get().createProdString("OwnedSubscribedListModule.emptyStateMessage")
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/javax/inject:javax.inject",
|
|
||||||
"configapi/configapi-core/src/main/scala/com/twitter/timelines/configapi",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/param/decider",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/decorator/urt",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product",
|
|
||||||
"util/util-core/src/main/scala/com/twitter/conversions",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,85 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following.param
|
|
||||||
|
|
||||||
import com.twitter.conversions.DurationOps._
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.WhoToFollowModuleDisplayType
|
|
||||||
import com.twitter.timelines.configapi.DurationConversion
|
|
||||||
import com.twitter.timelines.configapi.FSBoundedParam
|
|
||||||
import com.twitter.timelines.configapi.FSEnumParam
|
|
||||||
import com.twitter.timelines.configapi.FSParam
|
|
||||||
import com.twitter.timelines.configapi.HasDurationConversion
|
|
||||||
import com.twitter.util.Duration
|
|
||||||
|
|
||||||
object FollowingParam {
|
|
||||||
val SupportedClientFSName = "following_supported_client"
|
|
||||||
|
|
||||||
object ServerMaxResultsParam
|
|
||||||
extends FSBoundedParam[Int](
|
|
||||||
name = "following_server_max_results",
|
|
||||||
default = 100,
|
|
||||||
min = 1,
|
|
||||||
max = 500
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableWhoToFollowCandidatePipelineParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "following_enable_who_to_follow",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableAdsCandidatePipelineParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "following_enable_ads",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableFlipInjectionModuleCandidatePipelineParam
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "following_enable_flip_inline_injection_module",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
|
|
||||||
object FlipInlineInjectionModulePosition
|
|
||||||
extends FSBoundedParam[Int](
|
|
||||||
name = "following_flip_inline_injection_module_position",
|
|
||||||
default = 0,
|
|
||||||
min = 0,
|
|
||||||
max = 1000
|
|
||||||
)
|
|
||||||
|
|
||||||
object WhoToFollowPositionParam
|
|
||||||
extends FSBoundedParam[Int](
|
|
||||||
name = "following_who_to_follow_position",
|
|
||||||
default = 5,
|
|
||||||
min = 0,
|
|
||||||
max = 99
|
|
||||||
)
|
|
||||||
|
|
||||||
object WhoToFollowMinInjectionIntervalParam
|
|
||||||
extends FSBoundedParam[Duration](
|
|
||||||
"following_who_to_follow_min_injection_interval_in_minutes",
|
|
||||||
default = 1800.minutes,
|
|
||||||
min = 0.minutes,
|
|
||||||
max = 6000.minutes)
|
|
||||||
with HasDurationConversion {
|
|
||||||
override val durationConversion: DurationConversion = DurationConversion.FromMinutes
|
|
||||||
}
|
|
||||||
|
|
||||||
object WhoToFollowDisplayTypeIdParam
|
|
||||||
extends FSEnumParam[WhoToFollowModuleDisplayType.type](
|
|
||||||
name = "following_enable_who_to_follow_display_type_id",
|
|
||||||
default = WhoToFollowModuleDisplayType.Vertical,
|
|
||||||
enum = WhoToFollowModuleDisplayType
|
|
||||||
)
|
|
||||||
|
|
||||||
object WhoToFollowDisplayLocationParam
|
|
||||||
extends FSParam[String](
|
|
||||||
name = "following_who_to_follow_display_location",
|
|
||||||
default = "timeline_reverse_chron"
|
|
||||||
)
|
|
||||||
|
|
||||||
object EnableFastAds
|
|
||||||
extends FSParam[Boolean](
|
|
||||||
name = "following_enable_fast_ads",
|
|
||||||
default = true
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,34 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.following.param
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.param.decider.DeciderKey
|
|
||||||
import com.twitter.home_mixer.product.following.param.FollowingParam._
|
|
||||||
import com.twitter.product_mixer.core.product.ProductParamConfig
|
|
||||||
import com.twitter.servo.decider.DeciderKeyName
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class FollowingParamConfig @Inject() () extends ProductParamConfig {
|
|
||||||
override val enabledDeciderKey: DeciderKeyName = DeciderKey.EnableFollowingProduct
|
|
||||||
override val supportedClientFSName: String = SupportedClientFSName
|
|
||||||
|
|
||||||
override val booleanFSOverrides =
|
|
||||||
Seq(
|
|
||||||
EnableFlipInjectionModuleCandidatePipelineParam,
|
|
||||||
EnableWhoToFollowCandidatePipelineParam,
|
|
||||||
EnableAdsCandidatePipelineParam,
|
|
||||||
EnableFastAds,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val boundedIntFSOverrides = Seq(
|
|
||||||
FlipInlineInjectionModulePosition,
|
|
||||||
WhoToFollowPositionParam,
|
|
||||||
ServerMaxResultsParam
|
|
||||||
)
|
|
||||||
|
|
||||||
override val stringFSOverrides = Seq(WhoToFollowDisplayLocationParam)
|
|
||||||
|
|
||||||
override val boundedDurationFSOverrides = Seq(WhoToFollowMinInjectionIntervalParam)
|
|
||||||
|
|
||||||
override val enumFSOverrides = Seq(WhoToFollowDisplayTypeIdParam)
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/javax/inject:javax.inject",
|
|
||||||
"ads-injection/lib/src/main/scala/com/twitter/goldfinch/api",
|
|
||||||
"finagle/finagle-memcached/src/main/scala",
|
|
||||||
"finatra/inject/inject-core/src/main/scala",
|
|
||||||
"finatra/inject/inject-core/src/main/scala/com/twitter/inject",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/candidate_pipeline",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/candidate_source",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/decorator",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/decorator/builder",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/decorator/urt/builder",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/feature_hydrator",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/filter",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/gate",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/scorer",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/selector",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/side_effect",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/marshaller/timelines",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model/request",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/param",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/following/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/candidate_source",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/feature_hydrator",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/filter",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/functional_component/gate",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/param",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/query_transformer",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/response_transformer",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/scorer",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/side_effect",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/scored_tweets/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/service",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/util",
|
|
||||||
"home-mixer/thrift/src/main/thrift:thrift-scala",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/earlybird",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/timeline_scorer",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/candidate_source/timeline_service",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/decorator/urt",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/param_gated",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/tweet_is_nsfw",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/tweet_tweetypie",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/async",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/impressed_tweets",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/param_gated",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/query/social_graph",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/filter",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/gate",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/candidate/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/flexible_injection_pipeline",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/who_to_follow_module",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/pipeline/candidate/who_to_subscribe_module",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/premarshaller/urt",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/scorer/tweet_tlx",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/selector",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/selector/ads",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/side_effect",
|
|
||||||
"product-mixer/core/src/main/java/com/twitter/product_mixer/core/product/guice/scope",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/candidate_source/product_pipeline",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/configapi",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/decorator",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/decorator/urt/builder",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/marshaller/request",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/marshaller/response/urt",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/common/presentation",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/marshalling/request",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/marshalling/response/urt",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/model/marshalling/response/urt/item",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/candidate",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/mixer",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/product",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/recommendation",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/scoring",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/product/guice",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/quality_factor",
|
|
||||||
"src/java/com/twitter/search/common/util/lang",
|
|
||||||
"src/scala/com/twitter/suggests/controller_data",
|
|
||||||
"src/thrift/com/twitter/search:earlybird-scala",
|
|
||||||
"src/thrift/com/twitter/search/common:constants-java",
|
|
||||||
"src/thrift/com/twitter/timelines/render:thrift-scala",
|
|
||||||
"src/thrift/com/twitter/timelines/suggests/common:poly_data_record-java",
|
|
||||||
"src/thrift/com/twitter/timelineservice:thrift-scala",
|
|
||||||
"stitch/stitch-tweetypie",
|
|
||||||
"timelinemixer/server/src/main/scala/com/twitter/timelinemixer/injection/model/candidate",
|
|
||||||
"timelines/src/main/scala/com/twitter/timelines/injection/scribe",
|
|
||||||
"timelines/src/main/scala/com/twitter/timelines/model/candidate",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"src/thrift/com/twitter/timelines/render:thrift-scala",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,94 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.candidate.param_gated.ParamGatedCandidateFeatureHydrator
|
|
||||||
import com.twitter.adserver.{thriftscala => ads}
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeAdsClientEventDetailsBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.ExcludeSoftUserGate
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.EnableAdvertiserBrandSafetySettingsFeatureHydratorParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.AdsNumOrganicItemsParam
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.ads.AdsProdThriftCandidateSource
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.contextual_ref.ContextualTweetRefBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.ad.AdsCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.metadata.ClientEventInfoBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.candidate.ads.AdvertiserBrandSafetySettingsFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.ads.AdsCandidate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.AdsCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.AdsCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.StaticAdsDisplayLocationBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.ValidAdImpressionIdFilter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.CandidateScope
|
|
||||||
import com.twitter.product_mixer.core.gate.ParamNotGate
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.rtf.safety_level.TimelineHomePromotedHydrationSafetyLevel
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.contextual_ref.TweetHydrationContext
|
|
||||||
import com.twitter.timelines.injection.scribe.InjectionScribeUtil
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => st}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouAdsCandidatePipelineBuilder @Inject() (
|
|
||||||
adsCandidatePipelineConfigBuilder: AdsCandidatePipelineConfigBuilder,
|
|
||||||
adsCandidateSource: AdsProdThriftCandidateSource,
|
|
||||||
advertiserBrandSafetySettingsFeatureHydrator: AdvertiserBrandSafetySettingsFeatureHydrator[
|
|
||||||
ForYouQuery,
|
|
||||||
AdsCandidate
|
|
||||||
]) {
|
|
||||||
|
|
||||||
private val identifier: CandidatePipelineIdentifier = CandidatePipelineIdentifier("ForYouAds")
|
|
||||||
|
|
||||||
private val suggestType = st.SuggestType.Promoted
|
|
||||||
|
|
||||||
private val clientEventInfoBuilder = ClientEventInfoBuilder(
|
|
||||||
component = InjectionScribeUtil.scribeComponent(suggestType).get,
|
|
||||||
detailsBuilder = Some(HomeAdsClientEventDetailsBuilder(Some(suggestType.name)))
|
|
||||||
)
|
|
||||||
|
|
||||||
private val contextualTweetRefBuilder = ContextualTweetRefBuilder(
|
|
||||||
TweetHydrationContext(
|
|
||||||
safetyLevelOverride = Some(TimelineHomePromotedHydrationSafetyLevel),
|
|
||||||
outerTweetContext = None
|
|
||||||
))
|
|
||||||
|
|
||||||
private val decorator = UrtItemCandidateDecorator(
|
|
||||||
AdsCandidateUrtItemBuilder(
|
|
||||||
tweetClientEventInfoBuilder = Some(clientEventInfoBuilder),
|
|
||||||
contextualTweetRefBuilder = Some(contextualTweetRefBuilder)
|
|
||||||
))
|
|
||||||
|
|
||||||
private val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
|
|
||||||
def build(
|
|
||||||
organicCandidatePipelines: Option[CandidateScope] = None
|
|
||||||
): AdsCandidatePipelineConfig[ForYouQuery] =
|
|
||||||
adsCandidatePipelineConfigBuilder.build[ForYouQuery](
|
|
||||||
adsCandidateSource = adsCandidateSource,
|
|
||||||
identifier = identifier,
|
|
||||||
adsDisplayLocationBuilder = StaticAdsDisplayLocationBuilder(ads.DisplayLocation.TimelineHome),
|
|
||||||
estimateNumOrganicItems = _.params(AdsNumOrganicItemsParam).toShort,
|
|
||||||
gates = Seq(
|
|
||||||
ParamNotGate(
|
|
||||||
name = "AdsDisableInjectionBasedOnUserRole",
|
|
||||||
param = HomeGlobalParams.AdsDisableInjectionBasedOnUserRoleParam
|
|
||||||
),
|
|
||||||
ExcludeSoftUserGate
|
|
||||||
),
|
|
||||||
filters = Seq(ValidAdImpressionIdFilter),
|
|
||||||
postFilterFeatureHydration = Seq(
|
|
||||||
ParamGatedCandidateFeatureHydrator(
|
|
||||||
EnableAdvertiserBrandSafetySettingsFeatureHydratorParam,
|
|
||||||
advertiserBrandSafetySettingsFeatureHydrator
|
|
||||||
)
|
|
||||||
),
|
|
||||||
decorator = Some(decorator),
|
|
||||||
alerts = alerts,
|
|
||||||
urtRequest = Some(true),
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,111 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.adserver.{thriftscala => ads}
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeAdsClientEventDetailsBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.ExcludeSoftUserGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TweetLanguageFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TweetTextFeature
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.EnableAdvertiserBrandSafetySettingsFeatureHydratorParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.home_mixer.util.CandidatesUtil
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.ads.AdsProdThriftCandidateSource
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.contextual_ref.ContextualTweetRefBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.ad.AdsCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.metadata.ClientEventInfoBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.candidate.ads.AdvertiserBrandSafetySettingsFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.candidate.param_gated.ParamGatedCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptyCandidatesGate
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.ads.AdsCandidate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.AdsDependentCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.AdsDependentCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.CountTruncatedItemCandidatesFromPipelines
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.StaticAdsDisplayLocationBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.TruncatedPipelineScopedOrganicItems
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.ads.ValidAdImpressionIdFilter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.CandidateScope
|
|
||||||
import com.twitter.product_mixer.core.gate.ParamNotGate
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.rtf.safety_level.TimelineHomePromotedHydrationSafetyLevel
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.contextual_ref.TweetHydrationContext
|
|
||||||
import com.twitter.timelines.injection.scribe.InjectionScribeUtil
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => st}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouAdsDependentCandidatePipelineBuilder @Inject() (
|
|
||||||
adsCandidatePipelineConfigBuilder: AdsDependentCandidatePipelineConfigBuilder,
|
|
||||||
adsCandidateSource: AdsProdThriftCandidateSource,
|
|
||||||
advertiserBrandSafetySettingsFeatureHydrator: AdvertiserBrandSafetySettingsFeatureHydrator[
|
|
||||||
ForYouQuery,
|
|
||||||
AdsCandidate
|
|
||||||
]) {
|
|
||||||
|
|
||||||
private val identifier: CandidatePipelineIdentifier =
|
|
||||||
CandidatePipelineIdentifier("ForYouAdsDependent")
|
|
||||||
|
|
||||||
private val suggestType = st.SuggestType.Promoted
|
|
||||||
|
|
||||||
private val MaxOrganicTweets = 35
|
|
||||||
|
|
||||||
private val clientEventInfoBuilder = ClientEventInfoBuilder(
|
|
||||||
component = InjectionScribeUtil.scribeComponent(suggestType).get,
|
|
||||||
detailsBuilder = Some(HomeAdsClientEventDetailsBuilder(Some(suggestType.name)))
|
|
||||||
)
|
|
||||||
|
|
||||||
private val contextualTweetRefBuilder = ContextualTweetRefBuilder(
|
|
||||||
TweetHydrationContext(
|
|
||||||
safetyLevelOverride = Some(TimelineHomePromotedHydrationSafetyLevel),
|
|
||||||
outerTweetContext = None
|
|
||||||
))
|
|
||||||
|
|
||||||
private val decorator = UrtItemCandidateDecorator(
|
|
||||||
AdsCandidateUrtItemBuilder(
|
|
||||||
tweetClientEventInfoBuilder = Some(clientEventInfoBuilder),
|
|
||||||
contextualTweetRefBuilder = Some(contextualTweetRefBuilder)
|
|
||||||
))
|
|
||||||
|
|
||||||
private val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
|
|
||||||
def build(
|
|
||||||
organicCandidatePipelines: CandidateScope
|
|
||||||
): AdsDependentCandidatePipelineConfig[ForYouQuery] =
|
|
||||||
adsCandidatePipelineConfigBuilder.build[ForYouQuery](
|
|
||||||
adsCandidateSource = adsCandidateSource,
|
|
||||||
identifier = identifier,
|
|
||||||
adsDisplayLocationBuilder = StaticAdsDisplayLocationBuilder(ads.DisplayLocation.TimelineHome),
|
|
||||||
getOrganicItems = TruncatedPipelineScopedOrganicItems(
|
|
||||||
pipelines = organicCandidatePipelines,
|
|
||||||
textFeature = TweetTextFeature,
|
|
||||||
languageFeature = TweetLanguageFeature,
|
|
||||||
ordering = CandidatesUtil.scoreOrdering,
|
|
||||||
maxCount = MaxOrganicTweets
|
|
||||||
),
|
|
||||||
countNumOrganicItems =
|
|
||||||
CountTruncatedItemCandidatesFromPipelines(organicCandidatePipelines, MaxOrganicTweets),
|
|
||||||
gates = Seq(
|
|
||||||
ParamNotGate(
|
|
||||||
name = "AdsDisableInjectionBasedOnUserRole",
|
|
||||||
param = HomeGlobalParams.AdsDisableInjectionBasedOnUserRoleParam
|
|
||||||
),
|
|
||||||
ExcludeSoftUserGate,
|
|
||||||
NonEmptyCandidatesGate(organicCandidatePipelines)
|
|
||||||
),
|
|
||||||
filters = Seq(ValidAdImpressionIdFilter),
|
|
||||||
postFilterFeatureHydration = Seq(
|
|
||||||
ParamGatedCandidateFeatureHydrator(
|
|
||||||
EnableAdvertiserBrandSafetySettingsFeatureHydratorParam,
|
|
||||||
advertiserBrandSafetySettingsFeatureHydrator
|
|
||||||
)
|
|
||||||
),
|
|
||||||
decorator = Some(decorator),
|
|
||||||
alerts = alerts,
|
|
||||||
urtRequest = Some(true)
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,138 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.ConversationServiceResponseFeatureTransformer
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.HomeConversationServiceCandidateDecorator
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.InNetworkFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.NamesFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.TweetypieFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.InvalidConversationModuleFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.InvalidSubscriptionTweetFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.PreviouslyServedTweetsFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.RetweetDeduplicationFilter
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsHydratedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.QuotedTweetDroppedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TimelineServiceTweetsFeature
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.tweetconvosvc.ConversationServiceCandidateSource
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.tweetconvosvc.ConversationServiceCandidateSourceRequest
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.tweetconvosvc.TweetWithConversationMetadata
|
|
||||||
import com.twitter.product_mixer.component_library.filter.FeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.filter.PredicateFeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NoCandidatesGate
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptySeqFeatureGate
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.BaseCandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipelines
|
|
||||||
import com.twitter.product_mixer.core.functional_component.decorator.CandidateDecorator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BaseCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.filter.Filter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.BaseGate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineResultsTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.DependentCandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FilterIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.DependentCandidatePipelineConfig
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Candidate Pipeline Config that fetches Tweet ancestors from Conversation Service Candidate Source
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
class ForYouConversationServiceCandidatePipelineConfig @Inject() (
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig: ForYouScoredTweetsCandidatePipelineConfig,
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig: ForYouTimelineScorerCandidatePipelineConfig,
|
|
||||||
conversationServiceCandidateSource: ConversationServiceCandidateSource,
|
|
||||||
tweetypieFeatureHydrator: TweetypieFeatureHydrator,
|
|
||||||
namesFeatureHydrator: NamesFeatureHydrator,
|
|
||||||
invalidSubscriptionTweetFilter: InvalidSubscriptionTweetFilter,
|
|
||||||
homeFeedbackActionInfoBuilder: HomeFeedbackActionInfoBuilder)
|
|
||||||
extends DependentCandidatePipelineConfig[
|
|
||||||
ForYouQuery,
|
|
||||||
ConversationServiceCandidateSourceRequest,
|
|
||||||
TweetWithConversationMetadata,
|
|
||||||
TweetCandidate
|
|
||||||
] {
|
|
||||||
|
|
||||||
override val identifier: CandidatePipelineIdentifier =
|
|
||||||
CandidatePipelineIdentifier("ForYouConversationService")
|
|
||||||
|
|
||||||
override val gates: Seq[BaseGate[ForYouQuery]] = Seq(
|
|
||||||
NoCandidatesGate(
|
|
||||||
SpecificPipelines(
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig.identifier,
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig.identifier
|
|
||||||
)
|
|
||||||
),
|
|
||||||
NonEmptySeqFeatureGate(TimelineServiceTweetsFeature)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val candidateSource: BaseCandidateSource[
|
|
||||||
ConversationServiceCandidateSourceRequest,
|
|
||||||
TweetWithConversationMetadata
|
|
||||||
] = conversationServiceCandidateSource
|
|
||||||
|
|
||||||
override val queryTransformer: DependentCandidatePipelineQueryTransformer[
|
|
||||||
ForYouQuery,
|
|
||||||
ConversationServiceCandidateSourceRequest
|
|
||||||
] = { (query, candidates) =>
|
|
||||||
val timelineServiceTweets = query.features
|
|
||||||
.map(_.getOrElse(TimelineServiceTweetsFeature, Seq.empty)).getOrElse(Seq.empty)
|
|
||||||
|
|
||||||
val tweetsWithConversationMetadata = timelineServiceTweets.map { id =>
|
|
||||||
TweetWithConversationMetadata(
|
|
||||||
tweetId = id,
|
|
||||||
userId = None,
|
|
||||||
sourceTweetId = None,
|
|
||||||
sourceUserId = None,
|
|
||||||
inReplyToTweetId = None,
|
|
||||||
conversationId = None,
|
|
||||||
ancestors = Seq.empty
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ConversationServiceCandidateSourceRequest(tweetsWithConversationMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val featuresFromCandidateSourceTransformers: Seq[
|
|
||||||
CandidateFeatureTransformer[TweetWithConversationMetadata]
|
|
||||||
] = Seq(ConversationServiceResponseFeatureTransformer)
|
|
||||||
|
|
||||||
override val resultTransformer: CandidatePipelineResultsTransformer[
|
|
||||||
TweetWithConversationMetadata,
|
|
||||||
TweetCandidate
|
|
||||||
] = { sourceResult => TweetCandidate(id = sourceResult.tweetId) }
|
|
||||||
|
|
||||||
override val preFilterFeatureHydrationPhase1: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(
|
|
||||||
InNetworkFeatureHydrator,
|
|
||||||
tweetypieFeatureHydrator
|
|
||||||
)
|
|
||||||
|
|
||||||
override def filters: Seq[Filter[ForYouQuery, TweetCandidate]] = Seq(
|
|
||||||
PreviouslyServedTweetsFilter,
|
|
||||||
RetweetDeduplicationFilter,
|
|
||||||
FeatureFilter.fromFeature(FilterIdentifier("TweetypieHydrated"), IsHydratedFeature),
|
|
||||||
PredicateFeatureFilter.fromPredicate(
|
|
||||||
FilterIdentifier("QuotedTweetDropped"),
|
|
||||||
shouldKeepCandidate = { features => !features.getOrElse(QuotedTweetDroppedFeature, false) }
|
|
||||||
),
|
|
||||||
invalidSubscriptionTweetFilter,
|
|
||||||
InvalidConversationModuleFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
override val postFilterFeatureHydration: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(namesFeatureHydrator)
|
|
||||||
|
|
||||||
override val decorator: Option[CandidateDecorator[ForYouQuery, TweetCandidate]] =
|
|
||||||
HomeConversationServiceCandidateDecorator(homeFeedbackActionInfoBuilder)
|
|
||||||
|
|
||||||
override val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,142 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.conversions.DurationOps._
|
|
||||||
import com.twitter.home_mixer.marshaller.timelines.ChronologicalCursorUnmarshaller
|
|
||||||
import com.twitter.home_mixer.model.request.ForYouProduct
|
|
||||||
import com.twitter.home_mixer.model.request.ForYouProductContext
|
|
||||||
import com.twitter.home_mixer.model.request.HomeMixerRequest
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnablePushToHomeMixerPipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableScoredTweetsMixerPipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.ServerMaxResultsParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParamConfig
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAccessPolicy.DefaultHomeMixerAccessPolicy
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig.DefaultNotificationGroup
|
|
||||||
import com.twitter.product_mixer.component_library.model.cursor.UrtOrderedCursor
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.cursor.UrtCursorSerializer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.access_policy.AccessPolicy
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.Alert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.EmptyResponseRateAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.LatencyAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.P99
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.SuccessRateAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.ThroughputAlert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.predicate.TriggerIfAbove
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.predicate.TriggerIfBelow
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.predicate.TriggerIfLatencyAbove
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.ComponentIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.ProductPipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.request.Product
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.operation.TopCursor
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.BadRequest
|
|
||||||
import com.twitter.product_mixer.core.pipeline.pipeline_failure.PipelineFailure
|
|
||||||
import com.twitter.product_mixer.core.pipeline.product.ProductPipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.product.ProductParamConfig
|
|
||||||
import com.twitter.product_mixer.core.util.SortIndexBuilder
|
|
||||||
import com.twitter.timelines.configapi.Params
|
|
||||||
import com.twitter.timelines.render.{thriftscala => urt}
|
|
||||||
import com.twitter.timelines.util.RequestCursorSerializer
|
|
||||||
import com.twitter.util.Time
|
|
||||||
import com.twitter.util.Try
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouProductPipelineConfig @Inject() (
|
|
||||||
forYouTimelineScorerMixerPipelineConfig: ForYouTimelineScorerMixerPipelineConfig,
|
|
||||||
forYouScoredTweetsMixerPipelineConfig: ForYouScoredTweetsMixerPipelineConfig,
|
|
||||||
forYouPushToHomeMixerPipelineConfig: ForYouPushToHomeMixerPipelineConfig,
|
|
||||||
forYouParamConfig: ForYouParamConfig)
|
|
||||||
extends ProductPipelineConfig[HomeMixerRequest, ForYouQuery, urt.TimelineResponse] {
|
|
||||||
|
|
||||||
override val identifier: ProductPipelineIdentifier = ProductPipelineIdentifier("ForYou")
|
|
||||||
|
|
||||||
override val product: Product = ForYouProduct
|
|
||||||
|
|
||||||
override val paramConfig: ProductParamConfig = forYouParamConfig
|
|
||||||
|
|
||||||
override def pipelineQueryTransformer(
|
|
||||||
request: HomeMixerRequest,
|
|
||||||
params: Params
|
|
||||||
): ForYouQuery = {
|
|
||||||
val context = request.productContext match {
|
|
||||||
case Some(context: ForYouProductContext) => context
|
|
||||||
case _ => throw PipelineFailure(BadRequest, "ForYouProductContext not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
val debugOptions = request.debugParams.flatMap(_.debugOptions)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlike other clients, newly created tweets on Android have the sort index set to the current
|
|
||||||
* time instead of the top sort index + 1, so these tweets get stuck at the top of the timeline
|
|
||||||
* if subsequent timeline responses use the sort index from the previous response instead of
|
|
||||||
* the current time.
|
|
||||||
*/
|
|
||||||
val pipelineCursor = request.serializedRequestCursor.flatMap { cursor =>
|
|
||||||
Try(UrtCursorSerializer.deserializeOrderedCursor(cursor))
|
|
||||||
.getOrElse(ChronologicalCursorUnmarshaller(RequestCursorSerializer.deserialize(cursor)))
|
|
||||||
.map {
|
|
||||||
case topCursor @ UrtOrderedCursor(_, _, Some(TopCursor), _) =>
|
|
||||||
val queryTime = debugOptions.flatMap(_.requestTimeOverride).getOrElse(Time.now)
|
|
||||||
topCursor.copy(initialSortIndex = SortIndexBuilder.timeToId(queryTime))
|
|
||||||
case cursor => cursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ForYouQuery(
|
|
||||||
params = params,
|
|
||||||
clientContext = request.clientContext,
|
|
||||||
features = None,
|
|
||||||
pipelineCursor = pipelineCursor,
|
|
||||||
requestedMaxResults = Some(params(ServerMaxResultsParam)),
|
|
||||||
debugOptions = debugOptions,
|
|
||||||
deviceContext = context.deviceContext,
|
|
||||||
seenTweetIds = context.seenTweetIds,
|
|
||||||
dspClientContext = context.dspClientContext,
|
|
||||||
pushToHomeTweetId = context.pushToHomeTweetId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val pipelines: Seq[PipelineConfig] = Seq(
|
|
||||||
forYouTimelineScorerMixerPipelineConfig,
|
|
||||||
forYouScoredTweetsMixerPipelineConfig,
|
|
||||||
forYouPushToHomeMixerPipelineConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
override def pipelineSelector(
|
|
||||||
query: ForYouQuery
|
|
||||||
): ComponentIdentifier = {
|
|
||||||
if (query.pushToHomeTweetId.isDefined && query.params(EnablePushToHomeMixerPipelineParam))
|
|
||||||
forYouPushToHomeMixerPipelineConfig.identifier
|
|
||||||
else if (query.params(EnableScoredTweetsMixerPipelineParam))
|
|
||||||
forYouScoredTweetsMixerPipelineConfig.identifier
|
|
||||||
else forYouTimelineScorerMixerPipelineConfig.identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
override val alerts: Seq[Alert] = Seq(
|
|
||||||
SuccessRateAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
warnPredicate = TriggerIfBelow(99.9, 20, 30),
|
|
||||||
criticalPredicate = TriggerIfBelow(99.9, 30, 30),
|
|
||||||
),
|
|
||||||
LatencyAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
percentile = P99,
|
|
||||||
warnPredicate = TriggerIfLatencyAbove(2300.millis, 15, 30),
|
|
||||||
criticalPredicate = TriggerIfLatencyAbove(2800.millis, 15, 30)
|
|
||||||
),
|
|
||||||
ThroughputAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
warnPredicate = TriggerIfAbove(70000),
|
|
||||||
criticalPredicate = TriggerIfAbove(80000)
|
|
||||||
),
|
|
||||||
EmptyResponseRateAlert(
|
|
||||||
notificationGroup = DefaultNotificationGroup,
|
|
||||||
warnPredicate = TriggerIfAbove(2),
|
|
||||||
criticalPredicate = TriggerIfAbove(3)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val debugAccessPolicies: Set[AccessPolicy] = DefaultHomeMixerAccessPolicy
|
|
||||||
}
|
|
Binary file not shown.
@ -1,74 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.UrtDomainMarshaller
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.AddEntriesInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ClearCacheInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedBottomCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedTopCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ParamGatedIncludeInstruction
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.StaticTimelineScribeConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.UrtMetadataBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertAppendResults
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.TransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.response.urt.UrtTransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.premarshaller.DomainMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.selector.Selector
|
|
||||||
import com.twitter.product_mixer.core.model.common.UniversalNoun
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.MixerPipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.Timeline
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineScribeConfig
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.item.tweet.TweetItem
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.mixer.MixerPipelineConfig
|
|
||||||
import com.twitter.timelines.render.{thriftscala => urt}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouPushToHomeMixerPipelineConfig @Inject() (
|
|
||||||
forYouPushToHomeTweetCandidatePipelineConfig: ForYouPushToHomeTweetCandidatePipelineConfig,
|
|
||||||
urtTransportMarshaller: UrtTransportMarshaller)
|
|
||||||
extends MixerPipelineConfig[ForYouQuery, Timeline, urt.TimelineResponse] {
|
|
||||||
|
|
||||||
override val identifier: MixerPipelineIdentifier = MixerPipelineIdentifier("ForYouPushToHome")
|
|
||||||
|
|
||||||
override val candidatePipelines: Seq[CandidatePipelineConfig[ForYouQuery, _, _, _]] =
|
|
||||||
Seq(forYouPushToHomeTweetCandidatePipelineConfig)
|
|
||||||
|
|
||||||
override val resultSelectors: Seq[Selector[ForYouQuery]] =
|
|
||||||
Seq(InsertAppendResults(forYouPushToHomeTweetCandidatePipelineConfig.identifier))
|
|
||||||
|
|
||||||
override val domainMarshaller: DomainMarshaller[ForYouQuery, Timeline] = {
|
|
||||||
val instructionBuilders = Seq(
|
|
||||||
ClearCacheInstructionBuilder(
|
|
||||||
ParamGatedIncludeInstruction(ForYouParam.EnableClearCacheOnPushToHome)),
|
|
||||||
AddEntriesInstructionBuilder())
|
|
||||||
|
|
||||||
val idSelector: PartialFunction[UniversalNoun[_], Long] = { case item: TweetItem => item.id }
|
|
||||||
val topCursorBuilder = OrderedTopCursorBuilder(idSelector)
|
|
||||||
val bottomCursorBuilder = OrderedBottomCursorBuilder(idSelector)
|
|
||||||
|
|
||||||
val metadataBuilder = UrtMetadataBuilder(
|
|
||||||
title = None,
|
|
||||||
scribeConfigBuilder = Some(
|
|
||||||
StaticTimelineScribeConfigBuilder(
|
|
||||||
TimelineScribeConfig(
|
|
||||||
page = Some("for_you_push_to_home"),
|
|
||||||
section = None,
|
|
||||||
entityToken = None)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
UrtDomainMarshaller(
|
|
||||||
instructionBuilders = instructionBuilders,
|
|
||||||
metadataBuilder = Some(metadataBuilder),
|
|
||||||
cursorBuilders = Seq(topCursorBuilder, bottomCursorBuilder)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val transportMarshaller: TransportMarshaller[Timeline, urt.TimelineResponse] =
|
|
||||||
urtTransportMarshaller
|
|
||||||
}
|
|
Binary file not shown.
@ -1,81 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeClientEventInfoBuilder
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.SuggestTypeFeature
|
|
||||||
import com.twitter.home_mixer.product.for_you.functional_component.gate.PushToHomeRequestGate
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.tweet.TweetCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.CandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.PassthroughCandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.decorator.CandidateDecorator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.Gate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineResultsTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidateSourceIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.TransformerIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => st}
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouPushToHomeTweetCandidatePipelineConfig @Inject() ()
|
|
||||||
extends CandidatePipelineConfig[
|
|
||||||
ForYouQuery,
|
|
||||||
ForYouQuery,
|
|
||||||
TweetCandidate,
|
|
||||||
TweetCandidate
|
|
||||||
] {
|
|
||||||
|
|
||||||
override val identifier: CandidatePipelineIdentifier =
|
|
||||||
CandidatePipelineIdentifier("ForYouPushToHomeTweet")
|
|
||||||
|
|
||||||
override val gates: Seq[Gate[ForYouQuery]] = Seq(PushToHomeRequestGate)
|
|
||||||
|
|
||||||
override val queryTransformer: CandidatePipelineQueryTransformer[
|
|
||||||
ForYouQuery,
|
|
||||||
ForYouQuery
|
|
||||||
] = identity
|
|
||||||
|
|
||||||
override val featuresFromCandidateSourceTransformers: Seq[
|
|
||||||
CandidateFeatureTransformer[TweetCandidate]
|
|
||||||
] = Seq(new CandidateFeatureTransformer[TweetCandidate] {
|
|
||||||
override def features: Set[Feature[_, _]] = Set(SuggestTypeFeature)
|
|
||||||
|
|
||||||
override val identifier: TransformerIdentifier =
|
|
||||||
TransformerIdentifier("ForYouPushToHomeTweet")
|
|
||||||
|
|
||||||
override def transform(input: TweetCandidate): FeatureMap =
|
|
||||||
FeatureMapBuilder().add(SuggestTypeFeature, Some(st.SuggestType.Magicrec)).build()
|
|
||||||
})
|
|
||||||
|
|
||||||
override val resultTransformer: CandidatePipelineResultsTransformer[
|
|
||||||
TweetCandidate,
|
|
||||||
TweetCandidate
|
|
||||||
] = identity
|
|
||||||
|
|
||||||
override val candidateSource: CandidateSource[
|
|
||||||
ForYouQuery,
|
|
||||||
TweetCandidate
|
|
||||||
] = PassthroughCandidateSource(
|
|
||||||
CandidateSourceIdentifier("PushToHomeTweet"),
|
|
||||||
{ query => query.pushToHomeTweetId.toSeq.map(TweetCandidate(_)) }
|
|
||||||
)
|
|
||||||
|
|
||||||
override val decorator: Option[
|
|
||||||
CandidateDecorator[ForYouQuery, TweetCandidate]
|
|
||||||
] = {
|
|
||||||
val tweetItemBuilder = TweetCandidateUrtItemBuilder(
|
|
||||||
clientEventInfoBuilder = HomeClientEventInfoBuilder()
|
|
||||||
)
|
|
||||||
Some(UrtItemCandidateDecorator(tweetItemBuilder))
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,155 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeClientEventInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeConversationModuleMetadataBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeTimelinesScoreInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeTweetSocialContextBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.InNetworkFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.NamesFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.TweetypieFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.InvalidConversationModuleFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.InvalidSubscriptionTweetFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.SupportedLanguagesGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.ConversationModuleFocalTweetIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.InNetworkFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsHydratedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsNsfwFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.QuotedTweetDroppedFeature
|
|
||||||
import com.twitter.home_mixer.product.for_you.candidate_source.ScoredTweetWithConversationMetadata
|
|
||||||
import com.twitter.home_mixer.product.for_you.candidate_source.ScoredTweetsProductCandidateSource
|
|
||||||
import com.twitter.home_mixer.product.for_you.feature_hydrator.FocalTweetFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.for_you.filter.SocialContextFilter
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableScoredTweetsCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtMultipleModulesDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.tweet.TweetCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.ManualModuleId
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.StaticModuleDisplayTypeBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.TimelineModuleBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.filter.FeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.filter.PredicateFeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.CandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.decorator.CandidateDecorator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BaseCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.filter.Filter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.Gate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineResultsTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FilterIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.EntryNamespace
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.timeline_module.VerticalConversation
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.timelines.configapi.decider.DeciderParam
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouScoredTweetsCandidatePipelineConfig @Inject() (
|
|
||||||
scoredTweetsProductCandidateSource: ScoredTweetsProductCandidateSource,
|
|
||||||
focalTweetFeatureHydrator: FocalTweetFeatureHydrator,
|
|
||||||
namesFeatureHydrator: NamesFeatureHydrator,
|
|
||||||
tweetypieFeatureHydrator: TweetypieFeatureHydrator,
|
|
||||||
invalidSubscriptionTweetFilter: InvalidSubscriptionTweetFilter,
|
|
||||||
homeFeedbackActionInfoBuilder: HomeFeedbackActionInfoBuilder,
|
|
||||||
homeTweetSocialContextBuilder: HomeTweetSocialContextBuilder)
|
|
||||||
extends CandidatePipelineConfig[
|
|
||||||
ForYouQuery,
|
|
||||||
ForYouQuery,
|
|
||||||
ScoredTweetWithConversationMetadata,
|
|
||||||
TweetCandidate
|
|
||||||
] {
|
|
||||||
|
|
||||||
override val identifier: CandidatePipelineIdentifier =
|
|
||||||
CandidatePipelineIdentifier("ForYouScoredTweets")
|
|
||||||
|
|
||||||
private val TweetypieHydratedFilterId = "TweetypieHydrated"
|
|
||||||
private val QuotedTweetDroppedFilterId = "QuotedTweetDropped"
|
|
||||||
private val OutOfNetworkNSFWFilterId = "OutOfNetworkNSFW"
|
|
||||||
private val ConversationModuleNamespace = EntryNamespace("home-conversation")
|
|
||||||
|
|
||||||
override val gates: Seq[Gate[ForYouQuery]] = Seq(SupportedLanguagesGate)
|
|
||||||
|
|
||||||
override val candidateSource: CandidateSource[ForYouQuery, ScoredTweetWithConversationMetadata] =
|
|
||||||
scoredTweetsProductCandidateSource
|
|
||||||
|
|
||||||
override val enabledDeciderParam: Option[DeciderParam[Boolean]] =
|
|
||||||
Some(EnableScoredTweetsCandidatePipelineParam)
|
|
||||||
|
|
||||||
override val queryTransformer: CandidatePipelineQueryTransformer[ForYouQuery, ForYouQuery] =
|
|
||||||
identity
|
|
||||||
|
|
||||||
override val featuresFromCandidateSourceTransformers: Seq[
|
|
||||||
CandidateFeatureTransformer[ScoredTweetWithConversationMetadata]
|
|
||||||
] = Seq(ForYouScoredTweetsResponseFeatureTransformer)
|
|
||||||
|
|
||||||
override val resultTransformer: CandidatePipelineResultsTransformer[
|
|
||||||
ScoredTweetWithConversationMetadata,
|
|
||||||
TweetCandidate
|
|
||||||
] = { sourceResults => TweetCandidate(sourceResults.tweetId) }
|
|
||||||
|
|
||||||
override val preFilterFeatureHydrationPhase1: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(InNetworkFeatureHydrator, namesFeatureHydrator, tweetypieFeatureHydrator)
|
|
||||||
|
|
||||||
override val filters: Seq[Filter[ForYouQuery, TweetCandidate]] = Seq(
|
|
||||||
FeatureFilter.fromFeature(FilterIdentifier(TweetypieHydratedFilterId), IsHydratedFeature),
|
|
||||||
PredicateFeatureFilter.fromPredicate(
|
|
||||||
FilterIdentifier(QuotedTweetDroppedFilterId),
|
|
||||||
shouldKeepCandidate = { features => !features.getOrElse(QuotedTweetDroppedFeature, false) }
|
|
||||||
),
|
|
||||||
PredicateFeatureFilter.fromPredicate(
|
|
||||||
FilterIdentifier(OutOfNetworkNSFWFilterId),
|
|
||||||
shouldKeepCandidate = { features =>
|
|
||||||
features.getOrElse(InNetworkFeature, false) ||
|
|
||||||
!features.getOrElse(IsNsfwFeature, false)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
SocialContextFilter,
|
|
||||||
invalidSubscriptionTweetFilter,
|
|
||||||
InvalidConversationModuleFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
override val postFilterFeatureHydration: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(focalTweetFeatureHydrator)
|
|
||||||
|
|
||||||
override val decorator: Option[CandidateDecorator[ForYouQuery, TweetCandidate]] = {
|
|
||||||
val clientEventInfoBuilder = HomeClientEventInfoBuilder()
|
|
||||||
|
|
||||||
val tweetItemBuilder = TweetCandidateUrtItemBuilder(
|
|
||||||
clientEventInfoBuilder = clientEventInfoBuilder,
|
|
||||||
socialContextBuilder = Some(homeTweetSocialContextBuilder),
|
|
||||||
timelinesScoreInfoBuilder = Some(HomeTimelinesScoreInfoBuilder),
|
|
||||||
feedbackActionInfoBuilder = Some(homeFeedbackActionInfoBuilder)
|
|
||||||
)
|
|
||||||
|
|
||||||
val tweetDecorator = UrtItemCandidateDecorator(tweetItemBuilder)
|
|
||||||
|
|
||||||
val moduleBuilder = TimelineModuleBuilder(
|
|
||||||
entryNamespace = ConversationModuleNamespace,
|
|
||||||
clientEventInfoBuilder = clientEventInfoBuilder,
|
|
||||||
moduleIdGeneration = ManualModuleId(0L),
|
|
||||||
displayTypeBuilder = StaticModuleDisplayTypeBuilder(VerticalConversation),
|
|
||||||
metadataBuilder = Some(HomeConversationModuleMetadataBuilder())
|
|
||||||
)
|
|
||||||
|
|
||||||
Some(
|
|
||||||
UrtMultipleModulesDecorator(
|
|
||||||
urtItemCandidateDecorator = tweetDecorator,
|
|
||||||
moduleBuilder = moduleBuilder,
|
|
||||||
groupByKey = (_, _, candidateFeatures) =>
|
|
||||||
candidateFeatures.getOrElse(ConversationModuleFocalTweetIdFeature, None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
override val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert(10, 20)
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,376 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.clientapp.{thriftscala => ca}
|
|
||||||
import com.twitter.goldfinch.api.AdsInjectionSurfaceAreas
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.EditedTweetsCandidatePipelineConfig
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.NewTweetsPillCandidatePipelineConfig
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.AddEntriesWithReplaceAndShowAlertAndCoverInstructionBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator._
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.DebunchCandidates
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateConversationModuleId
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateHomeClientEventDetails
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateNewTweetsPillDecoration
|
|
||||||
import com.twitter.home_mixer.functional_component.side_effect._
|
|
||||||
import com.twitter.home_mixer.model.ClearCacheIncludeInstruction
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.InNetworkFeature
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.MaxNumberReplaceInstructionsParam
|
|
||||||
import com.twitter.home_mixer.param.HomeMixerFlagName.ScribeClientEventsFlag
|
|
||||||
import com.twitter.home_mixer.product.following.model.HomeMixerExternalStrings
|
|
||||||
import com.twitter.home_mixer.product.for_you.feature_hydrator.TimelineServiceTweetsQueryFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.ClearCacheOnPtr
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableFlipInjectionModuleCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.FlipInlineInjectionModulePosition
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.ServerMaxResultsParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.TweetPreviewsPositionParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToFollowPositionParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToSubscribePositionParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.side_effect.ServedCandidateFeatureKeysKafkaSideEffectBuilder
|
|
||||||
import com.twitter.home_mixer.product.for_you.side_effect.ServedCandidateKeysKafkaSideEffectBuilder
|
|
||||||
import com.twitter.home_mixer.product.for_you.side_effect.ServedStatsSideEffect
|
|
||||||
import com.twitter.home_mixer.util.CandidatesUtil
|
|
||||||
import com.twitter.inject.annotations.Flag
|
|
||||||
import com.twitter.logpipeline.client.common.EventPublisher
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.async.AsyncQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.PreviewCreatorsQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.SGSFollowedUsersQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.flexible_injection_pipeline.FlipPromptCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_subscribe_module.WhoToSubscribeCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.UrtDomainMarshaller
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ClearCacheInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedBottomCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedTopCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ReplaceAllEntries
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ReplaceEntryInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ShowAlertInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ShowCoverInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.StaticTimelineScribeConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.UrtMetadataBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropMaxCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropMaxModuleItemCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropModuleTooFewModuleItemResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertAppendResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertFixedPositionResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.SelectConditionally
|
|
||||||
import com.twitter.product_mixer.component_library.selector.UpdateSortCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.UpdateSortModuleItemCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.ads.AdsInjector
|
|
||||||
import com.twitter.product_mixer.component_library.selector.ads.InsertAdResults
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipeline
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipelines
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.StaticParam
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.QueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.TransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.response.urt.UrtTransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.premarshaller.DomainMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.selector.Selector
|
|
||||||
import com.twitter.product_mixer.core.functional_component.side_effect.PipelineResultSideEffect
|
|
||||||
import com.twitter.product_mixer.core.model.common.UniversalNoun
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.MixerPipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.presentation.ItemCandidateWithDetails
|
|
||||||
import com.twitter.product_mixer.core.model.common.presentation.ModuleCandidateWithDetails
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.Timeline
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineModule
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineScribeConfig
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.item.tweet.TweetItem
|
|
||||||
import com.twitter.product_mixer.core.pipeline.FailOpenPolicy
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.DependentCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.mixer.MixerPipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.product.guice.scope.ProductScoped
|
|
||||||
import com.twitter.stringcenter.client.StringCenter
|
|
||||||
import com.twitter.timelines.render.{thriftscala => urt}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Provider
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouScoredTweetsMixerPipelineConfig @Inject() (
|
|
||||||
forYouAdsDependentCandidatePipelineBuilder: ForYouAdsDependentCandidatePipelineBuilder,
|
|
||||||
forYouConversationServiceCandidatePipelineConfig: ForYouConversationServiceCandidatePipelineConfig,
|
|
||||||
forYouPushToHomeTweetCandidatePipelineConfig: ForYouPushToHomeTweetCandidatePipelineConfig,
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig: ForYouScoredTweetsCandidatePipelineConfig,
|
|
||||||
forYouWhoToFollowCandidatePipelineConfigBuilder: ForYouWhoToFollowCandidatePipelineConfigBuilder,
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfigBuilder: ForYouWhoToSubscribeCandidatePipelineConfigBuilder,
|
|
||||||
flipPromptCandidatePipelineConfigBuilder: FlipPromptCandidatePipelineConfigBuilder,
|
|
||||||
editedTweetsCandidatePipelineConfig: EditedTweetsCandidatePipelineConfig,
|
|
||||||
newTweetsPillCandidatePipelineConfig: NewTweetsPillCandidatePipelineConfig[ForYouQuery],
|
|
||||||
forYouTweetPreviewsCandidatePipelineConfig: ForYouTweetPreviewsCandidatePipelineConfig,
|
|
||||||
dismissInfoQueryFeatureHydrator: DismissInfoQueryFeatureHydrator,
|
|
||||||
gizmoduckUserQueryFeatureHydrator: GizmoduckUserQueryFeatureHydrator,
|
|
||||||
persistenceStoreQueryFeatureHydrator: PersistenceStoreQueryFeatureHydrator,
|
|
||||||
requestQueryFeatureHydrator: RequestQueryFeatureHydrator[ForYouQuery],
|
|
||||||
timelineServiceTweetsQueryFeatureHydrator: TimelineServiceTweetsQueryFeatureHydrator,
|
|
||||||
previewCreatorsQueryFeatureHydrator: PreviewCreatorsQueryFeatureHydrator,
|
|
||||||
sgsFollowedUsersQueryFeatureHydrator: SGSFollowedUsersQueryFeatureHydrator,
|
|
||||||
adsInjector: AdsInjector,
|
|
||||||
servedCandidateKeysKafkaSideEffectBuilder: ServedCandidateKeysKafkaSideEffectBuilder,
|
|
||||||
servedCandidateFeatureKeysKafkaSideEffectBuilder: ServedCandidateFeatureKeysKafkaSideEffectBuilder,
|
|
||||||
updateTimelinesPersistenceStoreSideEffect: UpdateTimelinesPersistenceStoreSideEffect,
|
|
||||||
truncateTimelinesPersistenceStoreSideEffect: TruncateTimelinesPersistenceStoreSideEffect,
|
|
||||||
homeScribeServedCandidatesSideEffect: HomeScribeServedCandidatesSideEffect,
|
|
||||||
servedStatsSideEffect: ServedStatsSideEffect,
|
|
||||||
clientEventsScribeEventPublisher: EventPublisher[ca.LogEvent],
|
|
||||||
externalStrings: HomeMixerExternalStrings,
|
|
||||||
@ProductScoped stringCenterProvider: Provider[StringCenter],
|
|
||||||
urtTransportMarshaller: UrtTransportMarshaller,
|
|
||||||
@Flag(ScribeClientEventsFlag) enableScribeClientEvents: Boolean)
|
|
||||||
extends MixerPipelineConfig[ForYouQuery, Timeline, urt.TimelineResponse] {
|
|
||||||
|
|
||||||
override val identifier: MixerPipelineIdentifier = MixerPipelineIdentifier("ForYouScoredTweets")
|
|
||||||
|
|
||||||
private val MaxConsecutiveOutOfNetworkCandidates = 2
|
|
||||||
|
|
||||||
private val PushToHomeTweetPosition = 0
|
|
||||||
|
|
||||||
private val dependentCandidatesStep = MixerPipelineConfig.dependentCandidatePipelinesStep
|
|
||||||
|
|
||||||
override val fetchQueryFeatures: Seq[QueryFeatureHydrator[ForYouQuery]] = Seq(
|
|
||||||
requestQueryFeatureHydrator,
|
|
||||||
persistenceStoreQueryFeatureHydrator,
|
|
||||||
timelineServiceTweetsQueryFeatureHydrator,
|
|
||||||
previewCreatorsQueryFeatureHydrator,
|
|
||||||
sgsFollowedUsersQueryFeatureHydrator,
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, dismissInfoQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, gizmoduckUserQueryFeatureHydrator),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val scoredTweetsCandidatePipelineScope =
|
|
||||||
SpecificPipeline(forYouScoredTweetsCandidatePipelineConfig.identifier)
|
|
||||||
|
|
||||||
private val forYouAdsCandidatePipelineConfig = forYouAdsDependentCandidatePipelineBuilder
|
|
||||||
.build(scoredTweetsCandidatePipelineScope)
|
|
||||||
|
|
||||||
private val forYouWhoToFollowCandidatePipelineConfig =
|
|
||||||
forYouWhoToFollowCandidatePipelineConfigBuilder.build()
|
|
||||||
|
|
||||||
private val forYouWhoToSubscribeCandidatePipelineConfig =
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfigBuilder.build()
|
|
||||||
|
|
||||||
private val flipPromptCandidatePipelineConfig =
|
|
||||||
flipPromptCandidatePipelineConfigBuilder.build[ForYouQuery](
|
|
||||||
supportedClientParam = Some(EnableFlipInjectionModuleCandidatePipelineParam)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val candidatePipelines: Seq[CandidatePipelineConfig[ForYouQuery, _, _, _]] = Seq(
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig,
|
|
||||||
forYouPushToHomeTweetCandidatePipelineConfig,
|
|
||||||
forYouWhoToFollowCandidatePipelineConfig,
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfig,
|
|
||||||
forYouTweetPreviewsCandidatePipelineConfig,
|
|
||||||
flipPromptCandidatePipelineConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
override val dependentCandidatePipelines: Seq[
|
|
||||||
DependentCandidatePipelineConfig[ForYouQuery, _, _, _]
|
|
||||||
] = Seq(
|
|
||||||
forYouAdsCandidatePipelineConfig,
|
|
||||||
forYouConversationServiceCandidatePipelineConfig,
|
|
||||||
editedTweetsCandidatePipelineConfig,
|
|
||||||
newTweetsPillCandidatePipelineConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
override val failOpenPolicies: Map[CandidatePipelineIdentifier, FailOpenPolicy] = Map(
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouAdsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouWhoToFollowCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouTweetPreviewsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
flipPromptCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
editedTweetsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
newTweetsPillCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val resultSelectors: Seq[Selector[ForYouQuery]] = Seq(
|
|
||||||
UpdateSortCandidates(
|
|
||||||
ordering = CandidatesUtil.reverseChronTweetsOrdering,
|
|
||||||
candidatePipeline = forYouConversationServiceCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
UpdateSortCandidates(
|
|
||||||
ordering = CandidatesUtil.scoreOrdering,
|
|
||||||
candidatePipeline = forYouScoredTweetsCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
UpdateSortModuleItemCandidates(
|
|
||||||
candidatePipeline = forYouScoredTweetsCandidatePipelineConfig.identifier,
|
|
||||||
ordering = CandidatesUtil.conversationModuleTweetsOrdering
|
|
||||||
),
|
|
||||||
DebunchCandidates(
|
|
||||||
pipelineScope = SpecificPipeline(forYouScoredTweetsCandidatePipelineConfig.identifier),
|
|
||||||
mustDebunch = {
|
|
||||||
case item: ItemCandidateWithDetails =>
|
|
||||||
!item.features.getOrElse(InNetworkFeature, false)
|
|
||||||
case module: ModuleCandidateWithDetails =>
|
|
||||||
!module.candidates.last.features.getOrElse(InNetworkFeature, false)
|
|
||||||
},
|
|
||||||
maxBunchSize = MaxConsecutiveOutOfNetworkCandidates
|
|
||||||
),
|
|
||||||
UpdateConversationModuleId(
|
|
||||||
pipelineScope = SpecificPipeline(forYouScoredTweetsCandidatePipelineConfig.identifier)
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = forYouConversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = ServerMaxResultsParam
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = forYouScoredTweetsCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = ServerMaxResultsParam
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = editedTweetsCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = MaxNumberReplaceInstructionsParam
|
|
||||||
),
|
|
||||||
DropMaxModuleItemCandidates(
|
|
||||||
candidatePipeline = forYouWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
maxModuleItemsParam = StaticParam(WhoToFollowCandidatePipelineConfig.MaxCandidatesSize)
|
|
||||||
),
|
|
||||||
DropMaxModuleItemCandidates(
|
|
||||||
candidatePipeline = forYouWhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
maxModuleItemsParam = StaticParam(WhoToSubscribeCandidatePipelineConfig.MaxCandidatesSize)
|
|
||||||
),
|
|
||||||
// The Conversation Service pipeline will only run if the Scored Tweets pipeline returned nothing
|
|
||||||
InsertAppendResults(
|
|
||||||
candidatePipeline = forYouConversationServiceCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
InsertAppendResults(
|
|
||||||
candidatePipeline = forYouScoredTweetsCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouTweetPreviewsCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = TweetPreviewsPositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = WhoToFollowPositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouWhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = WhoToSubscribePositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = flipPromptCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = FlipInlineInjectionModulePosition
|
|
||||||
),
|
|
||||||
// Insert Push To Home Tweet at top of Timeline
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouPushToHomeTweetCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = StaticParam(PushToHomeTweetPosition)
|
|
||||||
),
|
|
||||||
InsertAdResults(
|
|
||||||
surfaceAreaName = AdsInjectionSurfaceAreas.HomeTimeline,
|
|
||||||
adsInjector = adsInjector.forSurfaceArea(AdsInjectionSurfaceAreas.HomeTimeline),
|
|
||||||
adsCandidatePipeline = forYouAdsCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
// This selector must come after the tweets are inserted into the results
|
|
||||||
DropModuleTooFewModuleItemResults(
|
|
||||||
candidatePipeline = forYouWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
minModuleItemsParam = StaticParam(WhoToFollowCandidatePipelineConfig.MinCandidatesSize)
|
|
||||||
),
|
|
||||||
DropModuleTooFewModuleItemResults(
|
|
||||||
candidatePipeline = forYouWhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
minModuleItemsParam = StaticParam(WhoToSubscribeCandidatePipelineConfig.MinCandidatesSize)
|
|
||||||
),
|
|
||||||
UpdateNewTweetsPillDecoration(
|
|
||||||
pipelineScope = SpecificPipelines(
|
|
||||||
forYouConversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig.identifier,
|
|
||||||
newTweetsPillCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
stringCenter = stringCenterProvider.get(),
|
|
||||||
seeNewTweetsString = externalStrings.seeNewTweetsString,
|
|
||||||
tweetedString = externalStrings.tweetedString
|
|
||||||
),
|
|
||||||
InsertAppendResults(candidatePipeline = editedTweetsCandidatePipelineConfig.identifier),
|
|
||||||
SelectConditionally(
|
|
||||||
selector =
|
|
||||||
InsertAppendResults(candidatePipeline = newTweetsPillCandidatePipelineConfig.identifier),
|
|
||||||
includeSelector = (_, _, results) => CandidatesUtil.containsType[TweetCandidate](results)
|
|
||||||
),
|
|
||||||
UpdateHomeClientEventDetails(
|
|
||||||
candidatePipelines = Set(
|
|
||||||
forYouConversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig.identifier
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val servedCandidateKeysKafkaSideEffect =
|
|
||||||
servedCandidateKeysKafkaSideEffectBuilder.build(
|
|
||||||
Set(forYouScoredTweetsCandidatePipelineConfig.identifier))
|
|
||||||
|
|
||||||
private val servedCandidateFeatureKeysKafkaSideEffect =
|
|
||||||
servedCandidateFeatureKeysKafkaSideEffectBuilder.build(
|
|
||||||
Set(forYouScoredTweetsCandidatePipelineConfig.identifier))
|
|
||||||
|
|
||||||
private val homeScribeClientEventSideEffect = HomeScribeClientEventSideEffect(
|
|
||||||
enableScribeClientEvents = enableScribeClientEvents,
|
|
||||||
logPipelinePublisher = clientEventsScribeEventPublisher,
|
|
||||||
injectedTweetsCandidatePipelineIdentifiers = Seq(
|
|
||||||
forYouScoredTweetsCandidatePipelineConfig.identifier,
|
|
||||||
forYouConversationServiceCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
adsCandidatePipelineIdentifier = Some(forYouAdsCandidatePipelineConfig.identifier),
|
|
||||||
whoToFollowCandidatePipelineIdentifier = Some(
|
|
||||||
forYouWhoToFollowCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
whoToSubscribeCandidatePipelineIdentifier =
|
|
||||||
Some(forYouWhoToSubscribeCandidatePipelineConfig.identifier)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val resultSideEffects: Seq[PipelineResultSideEffect[ForYouQuery, Timeline]] = Seq(
|
|
||||||
servedCandidateKeysKafkaSideEffect,
|
|
||||||
servedCandidateFeatureKeysKafkaSideEffect,
|
|
||||||
updateTimelinesPersistenceStoreSideEffect,
|
|
||||||
truncateTimelinesPersistenceStoreSideEffect,
|
|
||||||
homeScribeClientEventSideEffect,
|
|
||||||
homeScribeServedCandidatesSideEffect,
|
|
||||||
servedStatsSideEffect
|
|
||||||
)
|
|
||||||
|
|
||||||
override val domainMarshaller: DomainMarshaller[ForYouQuery, Timeline] = {
|
|
||||||
val instructionBuilders = Seq(
|
|
||||||
ClearCacheInstructionBuilder(
|
|
||||||
ClearCacheIncludeInstruction(
|
|
||||||
ClearCacheOnPtr.EnableParam,
|
|
||||||
ClearCacheOnPtr.MinEntriesParam,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
ReplaceEntryInstructionBuilder(ReplaceAllEntries),
|
|
||||||
// excludes alert, cover, and replace candidates
|
|
||||||
AddEntriesWithReplaceAndShowAlertAndCoverInstructionBuilder(),
|
|
||||||
ShowAlertInstructionBuilder(),
|
|
||||||
ShowCoverInstructionBuilder(),
|
|
||||||
)
|
|
||||||
|
|
||||||
val idSelector: PartialFunction[UniversalNoun[_], Long] = {
|
|
||||||
// exclude ads while determining tweet cursor values
|
|
||||||
case item: TweetItem if item.promotedMetadata.isEmpty => item.id
|
|
||||||
case module: TimelineModule
|
|
||||||
if module.items.headOption.exists(_.item.isInstanceOf[TweetItem]) =>
|
|
||||||
module.items.last.item match { case item: TweetItem => item.id }
|
|
||||||
}
|
|
||||||
val topCursorBuilder = OrderedTopCursorBuilder(idSelector)
|
|
||||||
val bottomCursorBuilder = OrderedBottomCursorBuilder(idSelector)
|
|
||||||
|
|
||||||
val metadataBuilder = UrtMetadataBuilder(
|
|
||||||
title = None,
|
|
||||||
scribeConfigBuilder = Some(
|
|
||||||
StaticTimelineScribeConfigBuilder(
|
|
||||||
TimelineScribeConfig(
|
|
||||||
page = Some("for_you_scored_tweets"),
|
|
||||||
section = None,
|
|
||||||
entityToken = None)))
|
|
||||||
)
|
|
||||||
|
|
||||||
UrtDomainMarshaller(
|
|
||||||
instructionBuilders = instructionBuilders,
|
|
||||||
metadataBuilder = Some(metadataBuilder),
|
|
||||||
cursorBuilders = Seq(topCursorBuilder, bottomCursorBuilder)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val transportMarshaller: TransportMarshaller[Timeline, urt.TimelineResponse] =
|
|
||||||
urtTransportMarshaller
|
|
||||||
}
|
|
Binary file not shown.
@ -1,103 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures._
|
|
||||||
import com.twitter.home_mixer.product.for_you.candidate_source.ScoredTweetWithConversationMetadata
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.TransformerIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.metadata.BasicTopicContextFunctionalityType
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.metadata.RecWithEducationTopicContextFunctionalityType
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.metadata.RecommendationTopicContextFunctionalityType
|
|
||||||
import com.twitter.timelines.render.{thriftscala => tl}
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => tls}
|
|
||||||
|
|
||||||
object ForYouScoredTweetsResponseFeatureTransformer
|
|
||||||
extends CandidateFeatureTransformer[ScoredTweetWithConversationMetadata] {
|
|
||||||
|
|
||||||
override val identifier: TransformerIdentifier =
|
|
||||||
TransformerIdentifier("ForYouScoredTweetsResponse")
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] = Set(
|
|
||||||
AncestorsFeature,
|
|
||||||
AuthorIdFeature,
|
|
||||||
AuthorIsBlueVerifiedFeature,
|
|
||||||
AuthorIsCreatorFeature,
|
|
||||||
AuthorIsGoldVerifiedFeature,
|
|
||||||
AuthorIsGrayVerifiedFeature,
|
|
||||||
AuthorIsLegacyVerifiedFeature,
|
|
||||||
ConversationModuleFocalTweetIdFeature,
|
|
||||||
ConversationModuleIdFeature,
|
|
||||||
DirectedAtUserIdFeature,
|
|
||||||
ExclusiveConversationAuthorIdFeature,
|
|
||||||
FullScoringSucceededFeature,
|
|
||||||
FavoritedByUserIdsFeature,
|
|
||||||
FollowedByUserIdsFeature,
|
|
||||||
InNetworkFeature,
|
|
||||||
InReplyToTweetIdFeature,
|
|
||||||
InReplyToUserIdFeature,
|
|
||||||
IsAncestorCandidateFeature,
|
|
||||||
IsReadFromCacheFeature,
|
|
||||||
IsRetweetFeature,
|
|
||||||
PerspectiveFilteredLikedByUserIdsFeature,
|
|
||||||
QuotedTweetIdFeature,
|
|
||||||
QuotedUserIdFeature,
|
|
||||||
SGSValidFollowedByUserIdsFeature,
|
|
||||||
SGSValidLikedByUserIdsFeature,
|
|
||||||
ScoreFeature,
|
|
||||||
SourceTweetIdFeature,
|
|
||||||
SourceUserIdFeature,
|
|
||||||
StreamToKafkaFeature,
|
|
||||||
SuggestTypeFeature,
|
|
||||||
TopicContextFunctionalityTypeFeature,
|
|
||||||
TopicIdSocialContextFeature
|
|
||||||
)
|
|
||||||
|
|
||||||
override def transform(input: ScoredTweetWithConversationMetadata): FeatureMap =
|
|
||||||
FeatureMapBuilder()
|
|
||||||
.add(AncestorsFeature, input.ancestors.getOrElse(Seq.empty))
|
|
||||||
.add(AuthorIdFeature, Some(input.authorId))
|
|
||||||
.add(AuthorIsBlueVerifiedFeature, input.authorIsBlueVerified.getOrElse(false))
|
|
||||||
.add(AuthorIsGoldVerifiedFeature, input.authorIsGoldVerified.getOrElse(false))
|
|
||||||
.add(AuthorIsGrayVerifiedFeature, input.authorIsGrayVerified.getOrElse(false))
|
|
||||||
.add(AuthorIsLegacyVerifiedFeature, input.authorIsLegacyVerified.getOrElse(false))
|
|
||||||
.add(AuthorIsCreatorFeature, input.authorIsCreator.getOrElse(false))
|
|
||||||
.add(ConversationModuleIdFeature, input.conversationId)
|
|
||||||
.add(ConversationModuleFocalTweetIdFeature, input.conversationFocalTweetId)
|
|
||||||
.add(DirectedAtUserIdFeature, input.directedAtUserId)
|
|
||||||
.add(ExclusiveConversationAuthorIdFeature, input.exclusiveConversationAuthorId)
|
|
||||||
.add(SGSValidLikedByUserIdsFeature, input.sgsValidLikedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(SGSValidFollowedByUserIdsFeature, input.sgsValidFollowedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(FavoritedByUserIdsFeature, input.sgsValidLikedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(FollowedByUserIdsFeature, input.sgsValidFollowedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(FullScoringSucceededFeature, true)
|
|
||||||
.add(InNetworkFeature, input.inNetwork.getOrElse(true))
|
|
||||||
.add(InReplyToTweetIdFeature, input.inReplyToTweetId)
|
|
||||||
.add(InReplyToUserIdFeature, input.inReplyToUserId)
|
|
||||||
.add(IsAncestorCandidateFeature, input.conversationFocalTweetId.exists(_ != input.tweetId))
|
|
||||||
.add(IsReadFromCacheFeature, input.isReadFromCache.getOrElse(false))
|
|
||||||
.add(IsRetweetFeature, input.sourceTweetId.isDefined)
|
|
||||||
.add(
|
|
||||||
PerspectiveFilteredLikedByUserIdsFeature,
|
|
||||||
input.perspectiveFilteredLikedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(QuotedTweetIdFeature, input.quotedTweetId)
|
|
||||||
.add(QuotedUserIdFeature, input.quotedUserId)
|
|
||||||
.add(ScoreFeature, input.score)
|
|
||||||
.add(SourceTweetIdFeature, input.sourceTweetId)
|
|
||||||
.add(SourceUserIdFeature, input.sourceUserId)
|
|
||||||
.add(StreamToKafkaFeature, input.streamToKafka.getOrElse(false))
|
|
||||||
.add(SuggestTypeFeature, input.suggestType.orElse(Some(tls.SuggestType.Undefined)))
|
|
||||||
.add(
|
|
||||||
TopicContextFunctionalityTypeFeature,
|
|
||||||
input.topicFunctionalityType.collect {
|
|
||||||
case tl.TopicContextFunctionalityType.Basic => BasicTopicContextFunctionalityType
|
|
||||||
case tl.TopicContextFunctionalityType.Recommendation =>
|
|
||||||
RecommendationTopicContextFunctionalityType
|
|
||||||
case tl.TopicContextFunctionalityType.RecWithEducation =>
|
|
||||||
RecWithEducationTopicContextFunctionalityType
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.add(TopicIdSocialContextFeature, input.topicId)
|
|
||||||
.build()
|
|
||||||
}
|
|
Binary file not shown.
@ -1,241 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeClientEventInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeConversationModuleMetadataBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.builder.HomeTimelinesScoreInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeTweetSocialContextBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.InNetworkFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.NamesFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.PerspectiveFilteredSocialContextFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.SGSValidSocialContextFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.TweetypieFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.FeedbackFatigueFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.InvalidConversationModuleFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.InvalidSubscriptionTweetFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.RejectTweetFromViewerFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.RetweetDeduplicationFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.scorer.FeedbackFatigueScorer
|
|
||||||
import com.twitter.home_mixer.functional_component.scorer.OONTweetScalingScorer
|
|
||||||
import com.twitter.home_mixer.marshaller.timelines.DeviceContextMarshaller
|
|
||||||
import com.twitter.home_mixer.marshaller.timelines.TimelineServiceCursorMarshaller
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.ConversationModuleFocalTweetIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.InNetworkFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsHydratedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsNsfwFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.QuotedTweetDroppedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TimelineServiceTweetsFeature
|
|
||||||
import com.twitter.home_mixer.model.request.DeviceContext
|
|
||||||
import com.twitter.home_mixer.product.for_you.feature_hydrator.FocalTweetFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.for_you.filter.SocialContextFilter
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableTimelineScorerCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.timeline_scorer.ScoredTweetCandidateWithFocalTweet
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.timeline_scorer.TimelineScorerCandidateSource
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtMultipleModulesDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.tweet.TweetCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.ManualModuleId
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.StaticModuleDisplayTypeBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.TimelineModuleBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.filter.FeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.filter.PredicateFeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.BaseCandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.Alert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.decorator.CandidateDecorator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BaseCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.filter.Filter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.scorer.Scorer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineResultsTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FilterIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.EntryNamespace
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.timeline_module.VerticalConversation
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.timelines.configapi.FSParam
|
|
||||||
import com.twitter.timelines.model.candidate.CandidateTweetSourceId
|
|
||||||
import com.twitter.timelines.service.{thriftscala => tst}
|
|
||||||
import com.twitter.timelinescorer.{thriftscala => t}
|
|
||||||
import com.twitter.timelineservice.{thriftscala => tlst}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Candidate Pipeline Config that fetches tweets from the Timeline Scorer Candidate Source
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
class ForYouTimelineScorerCandidatePipelineConfig @Inject() (
|
|
||||||
timelineScorerCandidateSource: TimelineScorerCandidateSource,
|
|
||||||
deviceContextMarshaller: DeviceContextMarshaller,
|
|
||||||
tweetypieFeatureHydrator: TweetypieFeatureHydrator,
|
|
||||||
sgsValidSocialContextFeatureHydrator: SGSValidSocialContextFeatureHydrator,
|
|
||||||
perspectiveFilteredSocialContextFeatureHydrator: PerspectiveFilteredSocialContextFeatureHydrator,
|
|
||||||
namesFeatureHydrator: NamesFeatureHydrator,
|
|
||||||
focalTweetFeatureHydrator: FocalTweetFeatureHydrator,
|
|
||||||
invalidSubscriptionTweetFilter: InvalidSubscriptionTweetFilter,
|
|
||||||
homeFeedbackActionInfoBuilder: HomeFeedbackActionInfoBuilder,
|
|
||||||
homeTweetSocialContextBuilder: HomeTweetSocialContextBuilder)
|
|
||||||
extends CandidatePipelineConfig[
|
|
||||||
ForYouQuery,
|
|
||||||
t.ScoredTweetsRequest,
|
|
||||||
ScoredTweetCandidateWithFocalTweet,
|
|
||||||
TweetCandidate
|
|
||||||
] {
|
|
||||||
|
|
||||||
override val identifier: CandidatePipelineIdentifier =
|
|
||||||
CandidatePipelineIdentifier("ForYouTimelineScorerTweets")
|
|
||||||
|
|
||||||
private val TweetypieHydratedFilterId = "TweetypieHydrated"
|
|
||||||
private val QuotedTweetDroppedFilterId = "QuotedTweetDropped"
|
|
||||||
private val OutOfNetworkNSFWFilterId = "OutOfNetworkNSFW"
|
|
||||||
private val ConversationModuleNamespace = EntryNamespace("home-conversation")
|
|
||||||
|
|
||||||
override val supportedClientParam: Option[FSParam[Boolean]] =
|
|
||||||
Some(EnableTimelineScorerCandidatePipelineParam)
|
|
||||||
|
|
||||||
override val candidateSource: BaseCandidateSource[
|
|
||||||
t.ScoredTweetsRequest,
|
|
||||||
ScoredTweetCandidateWithFocalTweet
|
|
||||||
] = timelineScorerCandidateSource
|
|
||||||
|
|
||||||
override val queryTransformer: CandidatePipelineQueryTransformer[
|
|
||||||
ForYouQuery,
|
|
||||||
t.ScoredTweetsRequest
|
|
||||||
] = { query =>
|
|
||||||
val deviceContext = query.deviceContext.getOrElse(DeviceContext.Empty)
|
|
||||||
|
|
||||||
val scoredTweetsRequestContext = t.v1.ScoredTweetsRequestContext(
|
|
||||||
contextualUserId = query.clientContext.userId,
|
|
||||||
timelineId = query.clientContext.userId.map(tlst.TimelineId(tlst.TimelineType.Home, _, None)),
|
|
||||||
deviceContext = Some(deviceContextMarshaller(deviceContext, query.clientContext)),
|
|
||||||
seenTweetIds = query.seenTweetIds,
|
|
||||||
contextualUserContext = Some(tst.ContextualUserContext(query.clientContext.userRoles)),
|
|
||||||
timelineRequestCursor = query.pipelineCursor.flatMap(TimelineServiceCursorMarshaller(_))
|
|
||||||
)
|
|
||||||
|
|
||||||
val candidateTweetSourceIds = Seq(
|
|
||||||
CandidateTweetSourceId.RecycledTweet,
|
|
||||||
CandidateTweetSourceId.OrganicTweet,
|
|
||||||
CandidateTweetSourceId.AncestorsOnlyOrganicTweet,
|
|
||||||
CandidateTweetSourceId.BackfillOrganicTweet,
|
|
||||||
CandidateTweetSourceId.CroonTweet,
|
|
||||||
CandidateTweetSourceId.RecommendedTweet,
|
|
||||||
CandidateTweetSourceId.FrsTweet,
|
|
||||||
CandidateTweetSourceId.ListTweet,
|
|
||||||
CandidateTweetSourceId.RecommendedTrendTweet,
|
|
||||||
CandidateTweetSourceId.PopularTopicTweet
|
|
||||||
)
|
|
||||||
|
|
||||||
val timelineServiceTweets =
|
|
||||||
query.features.map(_.getOrElse(TimelineServiceTweetsFeature, Seq.empty)).getOrElse(Seq.empty)
|
|
||||||
|
|
||||||
val timelineEntries = timelineServiceTweets.map { id =>
|
|
||||||
tlst.TimelineEntry.Tweet(tlst.Tweet(statusId = id, sortIndex = id))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.ScoredTweetsRequest.V1(
|
|
||||||
t.v1.ScoredTweetsRequest(
|
|
||||||
scoredTweetsRequestContext = Some(scoredTweetsRequestContext),
|
|
||||||
candidateTweetSourceIds =
|
|
||||||
Some(candidateTweetSourceIds.flatMap(CandidateTweetSourceId.toThrift)),
|
|
||||||
maxResultsCount = query.requestedMaxResults,
|
|
||||||
organicTimeline = Some(
|
|
||||||
tlst.Timeline(
|
|
||||||
timelineId = tlst.TimelineId(
|
|
||||||
timelineType = tlst.TimelineType.Home,
|
|
||||||
id = query.getRequiredUserId,
|
|
||||||
canonicalTimelineId = None),
|
|
||||||
entries = timelineEntries,
|
|
||||||
modules = tlst.TimelineModules()
|
|
||||||
))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val featuresFromCandidateSourceTransformers: Seq[
|
|
||||||
CandidateFeatureTransformer[ScoredTweetCandidateWithFocalTweet]
|
|
||||||
] = Seq(ForYouTimelineScorerResponseFeatureTransformer)
|
|
||||||
|
|
||||||
override val resultTransformer: CandidatePipelineResultsTransformer[
|
|
||||||
ScoredTweetCandidateWithFocalTweet,
|
|
||||||
TweetCandidate
|
|
||||||
] = { candidateWithFocalTweetId =>
|
|
||||||
TweetCandidate(id = candidateWithFocalTweetId.candidate.tweetId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val preFilterFeatureHydrationPhase1: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(
|
|
||||||
namesFeatureHydrator,
|
|
||||||
tweetypieFeatureHydrator,
|
|
||||||
InNetworkFeatureHydrator,
|
|
||||||
sgsValidSocialContextFeatureHydrator,
|
|
||||||
perspectiveFilteredSocialContextFeatureHydrator,
|
|
||||||
)
|
|
||||||
|
|
||||||
override def filters: Seq[Filter[ForYouQuery, TweetCandidate]] = Seq(
|
|
||||||
RetweetDeduplicationFilter,
|
|
||||||
FeatureFilter.fromFeature(FilterIdentifier(TweetypieHydratedFilterId), IsHydratedFeature),
|
|
||||||
PredicateFeatureFilter.fromPredicate(
|
|
||||||
FilterIdentifier(QuotedTweetDroppedFilterId),
|
|
||||||
shouldKeepCandidate = { features => !features.getOrElse(QuotedTweetDroppedFeature, false) }
|
|
||||||
),
|
|
||||||
PredicateFeatureFilter.fromPredicate(
|
|
||||||
FilterIdentifier(OutOfNetworkNSFWFilterId),
|
|
||||||
shouldKeepCandidate = { features =>
|
|
||||||
features.getOrElse(InNetworkFeature, false) ||
|
|
||||||
!features.getOrElse(IsNsfwFeature, false)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
FeedbackFatigueFilter,
|
|
||||||
RejectTweetFromViewerFilter,
|
|
||||||
SocialContextFilter,
|
|
||||||
invalidSubscriptionTweetFilter,
|
|
||||||
InvalidConversationModuleFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
override val postFilterFeatureHydration: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(focalTweetFeatureHydrator)
|
|
||||||
|
|
||||||
override val scorers: Seq[Scorer[ForYouQuery, TweetCandidate]] =
|
|
||||||
Seq(OONTweetScalingScorer, FeedbackFatigueScorer)
|
|
||||||
|
|
||||||
override val decorator: Option[CandidateDecorator[ForYouQuery, TweetCandidate]] = {
|
|
||||||
val clientEventInfoBuilder = HomeClientEventInfoBuilder()
|
|
||||||
|
|
||||||
val tweetItemBuilder = TweetCandidateUrtItemBuilder(
|
|
||||||
clientEventInfoBuilder = clientEventInfoBuilder,
|
|
||||||
socialContextBuilder = Some(homeTweetSocialContextBuilder),
|
|
||||||
timelinesScoreInfoBuilder = Some(HomeTimelinesScoreInfoBuilder),
|
|
||||||
feedbackActionInfoBuilder = Some(homeFeedbackActionInfoBuilder)
|
|
||||||
)
|
|
||||||
|
|
||||||
val tweetDecorator = UrtItemCandidateDecorator(tweetItemBuilder)
|
|
||||||
|
|
||||||
val moduleBuilder = TimelineModuleBuilder(
|
|
||||||
entryNamespace = ConversationModuleNamespace,
|
|
||||||
clientEventInfoBuilder = clientEventInfoBuilder,
|
|
||||||
moduleIdGeneration = ManualModuleId(0L),
|
|
||||||
displayTypeBuilder = StaticModuleDisplayTypeBuilder(VerticalConversation),
|
|
||||||
metadataBuilder = Some(HomeConversationModuleMetadataBuilder())
|
|
||||||
)
|
|
||||||
|
|
||||||
Some(
|
|
||||||
UrtMultipleModulesDecorator(
|
|
||||||
urtItemCandidateDecorator = tweetDecorator,
|
|
||||||
moduleBuilder = moduleBuilder,
|
|
||||||
groupByKey = (_, _, candidateFeatures) =>
|
|
||||||
candidateFeatures.getOrElse(ConversationModuleFocalTweetIdFeature, None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
override val alerts: Seq[Alert] = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert(10, 20)
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,403 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.clientapp.{thriftscala => ca}
|
|
||||||
import com.twitter.goldfinch.api.AdsInjectionSurfaceAreas
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.EditedTweetsCandidatePipelineConfig
|
|
||||||
import com.twitter.home_mixer.candidate_pipeline.NewTweetsPillCandidatePipelineConfig
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.AddEntriesWithReplaceAndShowAlertAndCoverInstructionBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator.FeedbackHistoryQueryFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.functional_component.feature_hydrator._
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.DebunchCandidates
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateConversationModuleId
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateHomeClientEventDetails
|
|
||||||
import com.twitter.home_mixer.functional_component.selector.UpdateNewTweetsPillDecoration
|
|
||||||
import com.twitter.home_mixer.functional_component.side_effect._
|
|
||||||
import com.twitter.home_mixer.model.ClearCacheIncludeInstruction
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.InNetworkFeature
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.EnableImpressionBloomFilter
|
|
||||||
import com.twitter.home_mixer.param.HomeGlobalParams.MaxNumberReplaceInstructionsParam
|
|
||||||
import com.twitter.home_mixer.param.HomeMixerFlagName.ScribeClientEventsFlag
|
|
||||||
import com.twitter.home_mixer.product.following.model.HomeMixerExternalStrings
|
|
||||||
import com.twitter.home_mixer.product.for_you.feature_hydrator.TimelineServiceTweetsQueryFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.ClearCacheOnPtr
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableFlipInjectionModuleCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.FlipInlineInjectionModulePosition
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.ServerMaxResultsParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.TweetPreviewsPositionParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToFollowPositionParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToSubscribePositionParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.side_effect.ServedCandidateFeatureKeysKafkaSideEffectBuilder
|
|
||||||
import com.twitter.home_mixer.product.for_you.side_effect.ServedCandidateKeysKafkaSideEffectBuilder
|
|
||||||
import com.twitter.home_mixer.product.for_you.side_effect.ServedStatsSideEffect
|
|
||||||
import com.twitter.home_mixer.util.CandidatesUtil
|
|
||||||
import com.twitter.inject.annotations.Flag
|
|
||||||
import com.twitter.logpipeline.client.common.EventPublisher
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.async.AsyncQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.impressed_tweets.ImpressedTweetsQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.param_gated.AsyncParamGatedQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.PreviewCreatorsQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.SGSFollowedUsersQueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.flexible_injection_pipeline.FlipPromptCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_subscribe_module.WhoToSubscribeCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.UrtDomainMarshaller
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ClearCacheInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedBottomCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.OrderedTopCursorBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ReplaceAllEntries
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ReplaceEntryInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ShowAlertInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.ShowCoverInstructionBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.StaticTimelineScribeConfigBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.urt.builder.UrtMetadataBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropMaxCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropMaxModuleItemCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.DropModuleTooFewModuleItemResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertAppendResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.InsertFixedPositionResults
|
|
||||||
import com.twitter.product_mixer.component_library.selector.SelectConditionally
|
|
||||||
import com.twitter.product_mixer.component_library.selector.UpdateSortCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.UpdateSortModuleItemCandidates
|
|
||||||
import com.twitter.product_mixer.component_library.selector.ads.AdsInjector
|
|
||||||
import com.twitter.product_mixer.component_library.selector.ads.InsertAdResults
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipeline
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.SpecificPipelines
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.StaticParam
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.QueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.TransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.marshaller.response.urt.UrtTransportMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.premarshaller.DomainMarshaller
|
|
||||||
import com.twitter.product_mixer.core.functional_component.selector.Selector
|
|
||||||
import com.twitter.product_mixer.core.functional_component.side_effect.PipelineResultSideEffect
|
|
||||||
import com.twitter.product_mixer.core.model.common.UniversalNoun
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.MixerPipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.presentation.ItemCandidateWithDetails
|
|
||||||
import com.twitter.product_mixer.core.model.common.presentation.ModuleCandidateWithDetails
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.Timeline
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineModule
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.TimelineScribeConfig
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.item.tweet.TweetItem
|
|
||||||
import com.twitter.product_mixer.core.pipeline.FailOpenPolicy
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.DependentCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.pipeline.mixer.MixerPipelineConfig
|
|
||||||
import com.twitter.product_mixer.core.product.guice.scope.ProductScoped
|
|
||||||
import com.twitter.stringcenter.client.StringCenter
|
|
||||||
import com.twitter.timelines.render.{thriftscala => urt}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Provider
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouTimelineScorerMixerPipelineConfig @Inject() (
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig: ForYouTimelineScorerCandidatePipelineConfig,
|
|
||||||
forYouPushToHomeTweetCandidatePipelineConfig: ForYouPushToHomeTweetCandidatePipelineConfig,
|
|
||||||
forYouConversationServiceCandidatePipelineConfig: ForYouConversationServiceCandidatePipelineConfig,
|
|
||||||
forYouAdsDependentCandidatePipelineBuilder: ForYouAdsDependentCandidatePipelineBuilder,
|
|
||||||
forYouWhoToFollowCandidatePipelineConfigBuilder: ForYouWhoToFollowCandidatePipelineConfigBuilder,
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfigBuilder: ForYouWhoToSubscribeCandidatePipelineConfigBuilder,
|
|
||||||
flipPromptCandidatePipelineConfigBuilder: FlipPromptCandidatePipelineConfigBuilder,
|
|
||||||
editedTweetsCandidatePipelineConfig: EditedTweetsCandidatePipelineConfig,
|
|
||||||
newTweetsPillCandidatePipelineConfig: NewTweetsPillCandidatePipelineConfig[ForYouQuery],
|
|
||||||
forYouTweetPreviewsCandidatePipelineConfig: ForYouTweetPreviewsCandidatePipelineConfig,
|
|
||||||
dismissInfoQueryFeatureHydrator: DismissInfoQueryFeatureHydrator,
|
|
||||||
gizmoduckUserQueryFeatureHydrator: GizmoduckUserQueryFeatureHydrator,
|
|
||||||
impressionBloomFilterQueryFeatureHydrator: ImpressionBloomFilterQueryFeatureHydrator[ForYouQuery],
|
|
||||||
manhattanTweetImpressionsQueryFeatureHydrator: TweetImpressionsQueryFeatureHydrator[ForYouQuery],
|
|
||||||
memcacheTweetImpressionsQueryFeatureHydrator: ImpressedTweetsQueryFeatureHydrator,
|
|
||||||
persistenceStoreQueryFeatureHydrator: PersistenceStoreQueryFeatureHydrator,
|
|
||||||
requestQueryFeatureHydrator: RequestQueryFeatureHydrator[ForYouQuery],
|
|
||||||
timelineServiceTweetsQueryFeatureHydrator: TimelineServiceTweetsQueryFeatureHydrator,
|
|
||||||
lastNonPollingTimeQueryFeatureHydrator: LastNonPollingTimeQueryFeatureHydrator,
|
|
||||||
feedbackHistoryQueryFeatureHydrator: FeedbackHistoryQueryFeatureHydrator,
|
|
||||||
previewCreatorsQueryFeatureHydrator: PreviewCreatorsQueryFeatureHydrator,
|
|
||||||
sgsFollowedUsersQueryFeatureHydrator: SGSFollowedUsersQueryFeatureHydrator,
|
|
||||||
adsInjector: AdsInjector,
|
|
||||||
servedCandidateKeysKafkaSideEffectBuilder: ServedCandidateKeysKafkaSideEffectBuilder,
|
|
||||||
servedCandidateFeatureKeysKafkaSideEffectBuilder: ServedCandidateFeatureKeysKafkaSideEffectBuilder,
|
|
||||||
updateLastNonPollingTimeSideEffect: UpdateLastNonPollingTimeSideEffect[ForYouQuery, Timeline],
|
|
||||||
publishClientSentImpressionsEventBusSideEffect: PublishClientSentImpressionsEventBusSideEffect,
|
|
||||||
publishClientSentImpressionsManhattanSideEffect: PublishClientSentImpressionsManhattanSideEffect,
|
|
||||||
publishImpressionBloomFilterSideEffect: PublishImpressionBloomFilterSideEffect,
|
|
||||||
updateTimelinesPersistenceStoreSideEffect: UpdateTimelinesPersistenceStoreSideEffect,
|
|
||||||
truncateTimelinesPersistenceStoreSideEffect: TruncateTimelinesPersistenceStoreSideEffect,
|
|
||||||
homeScribeServedCandidatesSideEffect: HomeScribeServedCandidatesSideEffect,
|
|
||||||
servedStatsSideEffect: ServedStatsSideEffect,
|
|
||||||
clientEventsScribeEventPublisher: EventPublisher[ca.LogEvent],
|
|
||||||
externalStrings: HomeMixerExternalStrings,
|
|
||||||
@ProductScoped stringCenterProvider: Provider[StringCenter],
|
|
||||||
urtTransportMarshaller: UrtTransportMarshaller,
|
|
||||||
@Flag(ScribeClientEventsFlag) enableScribeClientEvents: Boolean)
|
|
||||||
extends MixerPipelineConfig[ForYouQuery, Timeline, urt.TimelineResponse] {
|
|
||||||
|
|
||||||
override val identifier: MixerPipelineIdentifier = MixerPipelineIdentifier("ForYouTimelineScorer")
|
|
||||||
|
|
||||||
private val MaxConsecutiveOutOfNetworkCandidates = 2
|
|
||||||
|
|
||||||
private val PushToHomeTweetPosition = 0
|
|
||||||
|
|
||||||
private val dependentCandidatesStep = MixerPipelineConfig.dependentCandidatePipelinesStep
|
|
||||||
private val resultSelectorsStep = MixerPipelineConfig.resultSelectorsStep
|
|
||||||
|
|
||||||
override def fetchQueryFeatures: Seq[QueryFeatureHydrator[ForYouQuery]] = Seq(
|
|
||||||
requestQueryFeatureHydrator,
|
|
||||||
persistenceStoreQueryFeatureHydrator,
|
|
||||||
timelineServiceTweetsQueryFeatureHydrator,
|
|
||||||
feedbackHistoryQueryFeatureHydrator,
|
|
||||||
previewCreatorsQueryFeatureHydrator,
|
|
||||||
sgsFollowedUsersQueryFeatureHydrator,
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, dismissInfoQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, gizmoduckUserQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(dependentCandidatesStep, lastNonPollingTimeQueryFeatureHydrator),
|
|
||||||
AsyncParamGatedQueryFeatureHydrator(
|
|
||||||
EnableImpressionBloomFilter,
|
|
||||||
resultSelectorsStep,
|
|
||||||
impressionBloomFilterQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(resultSelectorsStep, manhattanTweetImpressionsQueryFeatureHydrator),
|
|
||||||
AsyncQueryFeatureHydrator(resultSelectorsStep, memcacheTweetImpressionsQueryFeatureHydrator)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val timelineScorerCandidatePipelineScope =
|
|
||||||
SpecificPipeline(forYouTimelineScorerCandidatePipelineConfig.identifier)
|
|
||||||
|
|
||||||
private val forYouAdsCandidatePipelineConfig = forYouAdsDependentCandidatePipelineBuilder
|
|
||||||
.build(timelineScorerCandidatePipelineScope)
|
|
||||||
|
|
||||||
private val forYouWhoToFollowCandidatePipelineConfig =
|
|
||||||
forYouWhoToFollowCandidatePipelineConfigBuilder.build()
|
|
||||||
|
|
||||||
private val forYouWhoToSubscribeCandidatePipelineConfig =
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfigBuilder.build()
|
|
||||||
|
|
||||||
private val flipPromptCandidatePipelineConfig =
|
|
||||||
flipPromptCandidatePipelineConfigBuilder.build[ForYouQuery](
|
|
||||||
supportedClientParam = Some(EnableFlipInjectionModuleCandidatePipelineParam)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val candidatePipelines: Seq[CandidatePipelineConfig[ForYouQuery, _, _, _]] = Seq(
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig,
|
|
||||||
forYouPushToHomeTweetCandidatePipelineConfig,
|
|
||||||
forYouWhoToFollowCandidatePipelineConfig,
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfig,
|
|
||||||
forYouTweetPreviewsCandidatePipelineConfig,
|
|
||||||
flipPromptCandidatePipelineConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
override val dependentCandidatePipelines: Seq[
|
|
||||||
DependentCandidatePipelineConfig[ForYouQuery, _, _, _]
|
|
||||||
] = Seq(
|
|
||||||
forYouAdsCandidatePipelineConfig,
|
|
||||||
forYouConversationServiceCandidatePipelineConfig,
|
|
||||||
editedTweetsCandidatePipelineConfig,
|
|
||||||
newTweetsPillCandidatePipelineConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
override val failOpenPolicies: Map[CandidatePipelineIdentifier, FailOpenPolicy] = Map(
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouAdsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouWhoToFollowCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouWhoToSubscribeCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
forYouTweetPreviewsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
flipPromptCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
editedTweetsCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
newTweetsPillCandidatePipelineConfig.identifier -> FailOpenPolicy.Always,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val resultSelectors: Seq[Selector[ForYouQuery]] = Seq(
|
|
||||||
UpdateSortCandidates(
|
|
||||||
ordering = CandidatesUtil.reverseChronTweetsOrdering,
|
|
||||||
candidatePipeline = forYouConversationServiceCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
UpdateSortCandidates(
|
|
||||||
ordering = CandidatesUtil.scoreOrdering,
|
|
||||||
candidatePipeline = forYouTimelineScorerCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
UpdateSortModuleItemCandidates(
|
|
||||||
candidatePipeline = forYouTimelineScorerCandidatePipelineConfig.identifier,
|
|
||||||
ordering = CandidatesUtil.conversationModuleTweetsOrdering
|
|
||||||
),
|
|
||||||
DebunchCandidates(
|
|
||||||
pipelineScope = SpecificPipeline(forYouTimelineScorerCandidatePipelineConfig.identifier),
|
|
||||||
mustDebunch = {
|
|
||||||
case item: ItemCandidateWithDetails =>
|
|
||||||
!item.features.getOrElse(InNetworkFeature, false)
|
|
||||||
case module: ModuleCandidateWithDetails =>
|
|
||||||
!module.candidates.last.features.getOrElse(InNetworkFeature, false)
|
|
||||||
},
|
|
||||||
maxBunchSize = MaxConsecutiveOutOfNetworkCandidates
|
|
||||||
),
|
|
||||||
UpdateConversationModuleId(
|
|
||||||
pipelineScope = SpecificPipeline(forYouTimelineScorerCandidatePipelineConfig.identifier)
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = forYouConversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = ServerMaxResultsParam
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = forYouTimelineScorerCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = ServerMaxResultsParam
|
|
||||||
),
|
|
||||||
DropMaxCandidates(
|
|
||||||
candidatePipeline = editedTweetsCandidatePipelineConfig.identifier,
|
|
||||||
maxSelectionsParam = MaxNumberReplaceInstructionsParam
|
|
||||||
),
|
|
||||||
DropMaxModuleItemCandidates(
|
|
||||||
candidatePipeline = forYouWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
maxModuleItemsParam = StaticParam(WhoToFollowArmCandidatePipelineConfig.MaxCandidatesSize)
|
|
||||||
),
|
|
||||||
DropModuleTooFewModuleItemResults(
|
|
||||||
candidatePipeline = forYouWhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
minModuleItemsParam = StaticParam(WhoToSubscribeCandidatePipelineConfig.MinCandidatesSize)
|
|
||||||
),
|
|
||||||
DropMaxModuleItemCandidates(
|
|
||||||
candidatePipeline = forYouWhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
maxModuleItemsParam = StaticParam(WhoToSubscribeCandidatePipelineConfig.MaxCandidatesSize)
|
|
||||||
),
|
|
||||||
// Add Conversation Service tweets to results only if the scored pipeline doesn't return any
|
|
||||||
SelectConditionally(
|
|
||||||
selector = InsertAppendResults(
|
|
||||||
candidatePipeline = forYouConversationServiceCandidatePipelineConfig.identifier),
|
|
||||||
includeSelector = (_, candidates, _) =>
|
|
||||||
!candidates.exists(candidate =>
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig.identifier == candidate.source)
|
|
||||||
),
|
|
||||||
InsertAppendResults(candidatePipeline = forYouTimelineScorerCandidatePipelineConfig.identifier),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouTweetPreviewsCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = TweetPreviewsPositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = WhoToFollowPositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouWhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = WhoToSubscribePositionParam
|
|
||||||
),
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = flipPromptCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = FlipInlineInjectionModulePosition
|
|
||||||
),
|
|
||||||
// Insert Push To Home Tweet at top of Timeline
|
|
||||||
InsertFixedPositionResults(
|
|
||||||
candidatePipeline = forYouPushToHomeTweetCandidatePipelineConfig.identifier,
|
|
||||||
positionParam = StaticParam(PushToHomeTweetPosition)
|
|
||||||
),
|
|
||||||
InsertAdResults(
|
|
||||||
surfaceAreaName = AdsInjectionSurfaceAreas.HomeTimeline,
|
|
||||||
adsInjector = adsInjector.forSurfaceArea(AdsInjectionSurfaceAreas.HomeTimeline),
|
|
||||||
adsCandidatePipeline = forYouAdsCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
// This selector must come after the tweets are inserted into the results
|
|
||||||
DropModuleTooFewModuleItemResults(
|
|
||||||
candidatePipeline = forYouWhoToFollowCandidatePipelineConfig.identifier,
|
|
||||||
minModuleItemsParam = StaticParam(WhoToFollowArmCandidatePipelineConfig.MinCandidatesSize)
|
|
||||||
),
|
|
||||||
UpdateNewTweetsPillDecoration(
|
|
||||||
pipelineScope = SpecificPipelines(
|
|
||||||
forYouConversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig.identifier,
|
|
||||||
newTweetsPillCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
stringCenter = stringCenterProvider.get(),
|
|
||||||
seeNewTweetsString = externalStrings.seeNewTweetsString,
|
|
||||||
tweetedString = externalStrings.tweetedString
|
|
||||||
),
|
|
||||||
InsertAppendResults(candidatePipeline = editedTweetsCandidatePipelineConfig.identifier),
|
|
||||||
SelectConditionally(
|
|
||||||
selector =
|
|
||||||
InsertAppendResults(candidatePipeline = newTweetsPillCandidatePipelineConfig.identifier),
|
|
||||||
includeSelector = (_, _, results) => CandidatesUtil.containsType[TweetCandidate](results)
|
|
||||||
),
|
|
||||||
UpdateHomeClientEventDetails(
|
|
||||||
candidatePipelines = Set(
|
|
||||||
forYouConversationServiceCandidatePipelineConfig.identifier,
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig.identifier
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val servedCandidateKeysKafkaSideEffect =
|
|
||||||
servedCandidateKeysKafkaSideEffectBuilder.build(
|
|
||||||
Set(forYouTimelineScorerCandidatePipelineConfig.identifier))
|
|
||||||
|
|
||||||
private val servedCandidateFeatureKeysKafkaSideEffect =
|
|
||||||
servedCandidateFeatureKeysKafkaSideEffectBuilder.build(
|
|
||||||
Set(forYouTimelineScorerCandidatePipelineConfig.identifier))
|
|
||||||
|
|
||||||
private val homeScribeClientEventSideEffect = HomeScribeClientEventSideEffect(
|
|
||||||
enableScribeClientEvents = enableScribeClientEvents,
|
|
||||||
logPipelinePublisher = clientEventsScribeEventPublisher,
|
|
||||||
injectedTweetsCandidatePipelineIdentifiers = Seq(
|
|
||||||
forYouTimelineScorerCandidatePipelineConfig.identifier,
|
|
||||||
forYouConversationServiceCandidatePipelineConfig.identifier
|
|
||||||
),
|
|
||||||
adsCandidatePipelineIdentifier = Some(forYouAdsCandidatePipelineConfig.identifier),
|
|
||||||
whoToFollowCandidatePipelineIdentifier =
|
|
||||||
Some(forYouWhoToFollowCandidatePipelineConfig.identifier),
|
|
||||||
whoToSubscribeCandidatePipelineIdentifier =
|
|
||||||
Some(forYouWhoToSubscribeCandidatePipelineConfig.identifier)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val resultSideEffects: Seq[PipelineResultSideEffect[ForYouQuery, Timeline]] = Seq(
|
|
||||||
homeScribeClientEventSideEffect,
|
|
||||||
homeScribeServedCandidatesSideEffect,
|
|
||||||
publishClientSentImpressionsEventBusSideEffect,
|
|
||||||
publishClientSentImpressionsManhattanSideEffect,
|
|
||||||
publishImpressionBloomFilterSideEffect,
|
|
||||||
servedCandidateKeysKafkaSideEffect,
|
|
||||||
servedCandidateFeatureKeysKafkaSideEffect,
|
|
||||||
servedStatsSideEffect,
|
|
||||||
truncateTimelinesPersistenceStoreSideEffect,
|
|
||||||
updateLastNonPollingTimeSideEffect,
|
|
||||||
updateTimelinesPersistenceStoreSideEffect
|
|
||||||
)
|
|
||||||
|
|
||||||
override val domainMarshaller: DomainMarshaller[ForYouQuery, Timeline] = {
|
|
||||||
val instructionBuilders = Seq(
|
|
||||||
ClearCacheInstructionBuilder(
|
|
||||||
ClearCacheIncludeInstruction(
|
|
||||||
ClearCacheOnPtr.EnableParam,
|
|
||||||
ClearCacheOnPtr.MinEntriesParam
|
|
||||||
)
|
|
||||||
),
|
|
||||||
ReplaceEntryInstructionBuilder(ReplaceAllEntries),
|
|
||||||
// excludes alert, cover, and replace candidates
|
|
||||||
AddEntriesWithReplaceAndShowAlertAndCoverInstructionBuilder(),
|
|
||||||
ShowAlertInstructionBuilder(),
|
|
||||||
ShowCoverInstructionBuilder(),
|
|
||||||
)
|
|
||||||
|
|
||||||
val idSelector: PartialFunction[UniversalNoun[_], Long] = {
|
|
||||||
// exclude ads while determining tweet cursor values
|
|
||||||
case item: TweetItem if item.promotedMetadata.isEmpty => item.id
|
|
||||||
case module: TimelineModule
|
|
||||||
if module.items.headOption.exists(_.item.isInstanceOf[TweetItem]) =>
|
|
||||||
module.items.last.item match { case item: TweetItem => item.id }
|
|
||||||
}
|
|
||||||
val topCursorBuilder = OrderedTopCursorBuilder(idSelector)
|
|
||||||
val bottomCursorBuilder = OrderedBottomCursorBuilder(idSelector)
|
|
||||||
|
|
||||||
val metadataBuilder = UrtMetadataBuilder(
|
|
||||||
title = None,
|
|
||||||
scribeConfigBuilder = Some(
|
|
||||||
StaticTimelineScribeConfigBuilder(
|
|
||||||
TimelineScribeConfig(
|
|
||||||
page = Some("for_you_timeline_scorer"),
|
|
||||||
section = None,
|
|
||||||
entityToken = None)))
|
|
||||||
)
|
|
||||||
|
|
||||||
UrtDomainMarshaller(
|
|
||||||
instructionBuilders = instructionBuilders,
|
|
||||||
metadataBuilder = Some(metadataBuilder),
|
|
||||||
cursorBuilders = Seq(topCursorBuilder, bottomCursorBuilder)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val transportMarshaller: TransportMarshaller[Timeline, urt.TimelineResponse] =
|
|
||||||
urtTransportMarshaller
|
|
||||||
}
|
|
Binary file not shown.
@ -1,199 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.tweetconvosvc.tweet_ancestor.{thriftscala => ta}
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures._
|
|
||||||
import com.twitter.mediaservices.commons.tweetmedia.{thriftscala => mt}
|
|
||||||
import com.twitter.product_mixer.component_library.candidate_source.timeline_scorer.ScoredTweetCandidateWithFocalTweet
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.TransformerIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.metadata.BasicTopicContextFunctionalityType
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.metadata.RecWithEducationTopicContextFunctionalityType
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.metadata.RecommendationTopicContextFunctionalityType
|
|
||||||
import com.twitter.search.common.constants.thriftjava.ThriftLanguage
|
|
||||||
import com.twitter.search.common.util.lang.ThriftLanguageUtil
|
|
||||||
import com.twitter.snowflake.id.SnowflakeId
|
|
||||||
import com.twitter.timelinemixer.injection.model.candidate.AudioSpaceMetaData
|
|
||||||
import com.twitter.timelines.conversation_features.{thriftscala => cvt}
|
|
||||||
import com.twitter.timelinescorer.common.scoredtweetcandidate.{thriftscala => stc}
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => tls}
|
|
||||||
|
|
||||||
object ForYouTimelineScorerResponseFeatureTransformer
|
|
||||||
extends CandidateFeatureTransformer[ScoredTweetCandidateWithFocalTweet] {
|
|
||||||
|
|
||||||
override val identifier: TransformerIdentifier =
|
|
||||||
TransformerIdentifier("ForYouTimelineScorerResponse")
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] = Set(
|
|
||||||
AncestorsFeature,
|
|
||||||
AudioSpaceMetaDataFeature,
|
|
||||||
AuthorIdFeature,
|
|
||||||
AuthorIsBlueVerifiedFeature,
|
|
||||||
AuthorIsCreatorFeature,
|
|
||||||
AuthorIsGoldVerifiedFeature,
|
|
||||||
AuthorIsGrayVerifiedFeature,
|
|
||||||
AuthorIsLegacyVerifiedFeature,
|
|
||||||
AuthoredByContextualUserFeature,
|
|
||||||
CandidateSourceIdFeature,
|
|
||||||
ConversationFeature,
|
|
||||||
ConversationModuleFocalTweetIdFeature,
|
|
||||||
ConversationModuleIdFeature,
|
|
||||||
DirectedAtUserIdFeature,
|
|
||||||
EarlybirdFeature,
|
|
||||||
EntityTokenFeature,
|
|
||||||
ExclusiveConversationAuthorIdFeature,
|
|
||||||
FavoritedByUserIdsFeature,
|
|
||||||
FollowedByUserIdsFeature,
|
|
||||||
TopicIdSocialContextFeature,
|
|
||||||
TopicContextFunctionalityTypeFeature,
|
|
||||||
FromInNetworkSourceFeature,
|
|
||||||
FullScoringSucceededFeature,
|
|
||||||
HasDisplayedTextFeature,
|
|
||||||
InNetworkFeature,
|
|
||||||
InReplyToTweetIdFeature,
|
|
||||||
IsAncestorCandidateFeature,
|
|
||||||
IsExtendedReplyFeature,
|
|
||||||
IsRandomTweetFeature,
|
|
||||||
IsReadFromCacheFeature,
|
|
||||||
IsRetweetFeature,
|
|
||||||
IsRetweetedReplyFeature,
|
|
||||||
NonSelfFavoritedByUserIdsFeature,
|
|
||||||
NumImagesFeature,
|
|
||||||
OriginalTweetCreationTimeFromSnowflakeFeature,
|
|
||||||
PredictionRequestIdFeature,
|
|
||||||
QuotedTweetIdFeature,
|
|
||||||
ScoreFeature,
|
|
||||||
SimclustersTweetTopKClustersWithScoresFeature,
|
|
||||||
SourceTweetIdFeature,
|
|
||||||
SourceUserIdFeature,
|
|
||||||
StreamToKafkaFeature,
|
|
||||||
SuggestTypeFeature,
|
|
||||||
TweetLanguageFeature,
|
|
||||||
VideoDurationMsFeature,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Convert language code to ISO 639-3 format
|
|
||||||
private def getLanguageISOFormatByValue(languageCodeValue: Int): String =
|
|
||||||
ThriftLanguageUtil.getLanguageCodeOf(ThriftLanguage.findByValue(languageCodeValue))
|
|
||||||
|
|
||||||
override def transform(
|
|
||||||
candidateWithFocalTweet: ScoredTweetCandidateWithFocalTweet
|
|
||||||
): FeatureMap = {
|
|
||||||
val candidate: stc.v1.ScoredTweetCandidate = candidateWithFocalTweet.candidate
|
|
||||||
val focalTweetId = candidateWithFocalTweet.focalTweetIdOpt
|
|
||||||
|
|
||||||
val originalTweetId = candidate.sourceTweetId.getOrElse(candidate.tweetId)
|
|
||||||
val tweetFeatures = candidate.tweetFeaturesMap.flatMap(_.get(originalTweetId))
|
|
||||||
val earlybirdFeatures = tweetFeatures.flatMap(_.recapFeatures.flatMap(_.tweetFeatures))
|
|
||||||
val directedAtUserIsInFirstDegree =
|
|
||||||
earlybirdFeatures.flatMap(_.directedAtUserIdIsInFirstDegree)
|
|
||||||
val isReply = candidate.inReplyToTweetId.nonEmpty
|
|
||||||
val isRetweet = candidate.isRetweet.getOrElse(false)
|
|
||||||
val isInNetwork = candidate.isInNetwork.getOrElse(true)
|
|
||||||
val conversationFeatures = candidate.conversationFeatures.flatMap {
|
|
||||||
case cvt.ConversationFeatures.V1(candidate) => Some(candidate)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
val numImages = candidate.mediaMetaData
|
|
||||||
.map(
|
|
||||||
_.count(mediaEntity =>
|
|
||||||
mediaEntity.mediaInfo.exists(_.isInstanceOf[mt.MediaInfo.ImageInfo]) ||
|
|
||||||
mediaEntity.mediaInfo.isEmpty))
|
|
||||||
val hasImage = earlybirdFeatures.exists(_.hasImage)
|
|
||||||
val hasVideo = earlybirdFeatures.exists(_.hasVideo)
|
|
||||||
val hasCard = earlybirdFeatures.exists(_.hasCard)
|
|
||||||
val hasQuote = earlybirdFeatures.exists(_.hasQuote.contains(true))
|
|
||||||
val hasDisplayedText = earlybirdFeatures.exists(_.tweetLength.exists(length => {
|
|
||||||
val numMedia = Seq(hasVideo, (hasImage || hasCard), hasQuote).count(b => b)
|
|
||||||
val tcoLengthsPlusSpaces = 23 * numMedia + (if (numMedia > 0) numMedia - 1 else 0)
|
|
||||||
length > tcoLengthsPlusSpaces
|
|
||||||
}))
|
|
||||||
val suggestType = candidate.overrideSuggestType.orElse(Some(tls.SuggestType.Undefined))
|
|
||||||
|
|
||||||
val topicSocialProofMetadataOpt = candidate.entityData.flatMap(_.topicSocialProofMetadata)
|
|
||||||
val topicIdSocialContextOpt = topicSocialProofMetadataOpt.map(_.topicId)
|
|
||||||
val topicContextFunctionalityTypeOpt =
|
|
||||||
topicSocialProofMetadataOpt.map(_.topicContextFunctionalityType).collect {
|
|
||||||
case stc.v1.TopicContextFunctionalityType.Basic => BasicTopicContextFunctionalityType
|
|
||||||
case stc.v1.TopicContextFunctionalityType.Recommendation =>
|
|
||||||
RecommendationTopicContextFunctionalityType
|
|
||||||
case stc.v1.TopicContextFunctionalityType.RecWithEducation =>
|
|
||||||
RecWithEducationTopicContextFunctionalityType
|
|
||||||
}
|
|
||||||
|
|
||||||
FeatureMapBuilder()
|
|
||||||
.add(
|
|
||||||
AncestorsFeature,
|
|
||||||
candidate.ancestors
|
|
||||||
.getOrElse(Seq.empty)
|
|
||||||
.map(ancestor => ta.TweetAncestor(ancestor.tweetId, ancestor.userId.getOrElse(0L))))
|
|
||||||
.add(
|
|
||||||
AudioSpaceMetaDataFeature,
|
|
||||||
candidate.audioSpaceMetaDatalist.map(_.head).map(AudioSpaceMetaData.fromThrift))
|
|
||||||
.add(AuthorIdFeature, Some(candidate.authorId))
|
|
||||||
.add(AuthorIsBlueVerifiedFeature, candidate.authorIsBlueVerified.getOrElse(false))
|
|
||||||
.add(
|
|
||||||
AuthorIsCreatorFeature,
|
|
||||||
candidate.authorIsCreator.getOrElse(false)
|
|
||||||
)
|
|
||||||
.add(AuthorIsGoldVerifiedFeature, candidate.authorIsGoldVerified.getOrElse(false))
|
|
||||||
.add(AuthorIsGrayVerifiedFeature, candidate.authorIsGrayVerified.getOrElse(false))
|
|
||||||
.add(AuthorIsLegacyVerifiedFeature, candidate.authorIsLegacyVerified.getOrElse(false))
|
|
||||||
.add(
|
|
||||||
AuthoredByContextualUserFeature,
|
|
||||||
candidate.viewerId.contains(candidate.authorId) ||
|
|
||||||
candidate.viewerId.exists(candidate.sourceUserId.contains))
|
|
||||||
.add(CandidateSourceIdFeature, candidate.candidateTweetSourceId)
|
|
||||||
.add(ConversationFeature, conversationFeatures)
|
|
||||||
.add(ConversationModuleIdFeature, candidate.conversationId)
|
|
||||||
.add(ConversationModuleFocalTweetIdFeature, focalTweetId)
|
|
||||||
.add(DirectedAtUserIdFeature, candidate.directedAtUserId)
|
|
||||||
.add(EarlybirdFeature, earlybirdFeatures)
|
|
||||||
// This is temporary, will need to be updated with the encoded string.
|
|
||||||
.add(EntityTokenFeature, Some("test_EntityTokenForYou"))
|
|
||||||
.add(ExclusiveConversationAuthorIdFeature, candidate.exclusiveConversationAuthorId)
|
|
||||||
.add(FavoritedByUserIdsFeature, candidate.favoritedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(FollowedByUserIdsFeature, candidate.followedByUserIds.getOrElse(Seq.empty))
|
|
||||||
.add(TopicIdSocialContextFeature, topicIdSocialContextOpt)
|
|
||||||
.add(TopicContextFunctionalityTypeFeature, topicContextFunctionalityTypeOpt)
|
|
||||||
.add(FullScoringSucceededFeature, candidate.fullScoringSucceeded.getOrElse(false))
|
|
||||||
.add(HasDisplayedTextFeature, hasDisplayedText)
|
|
||||||
.add(InNetworkFeature, candidate.isInNetwork.getOrElse(true))
|
|
||||||
.add(InReplyToTweetIdFeature, candidate.inReplyToTweetId)
|
|
||||||
.add(IsAncestorCandidateFeature, candidate.isAncestorCandidate.getOrElse(false))
|
|
||||||
.add(
|
|
||||||
IsExtendedReplyFeature,
|
|
||||||
isInNetwork && isReply && !isRetweet && directedAtUserIsInFirstDegree.contains(false))
|
|
||||||
.add(FromInNetworkSourceFeature, candidate.isInNetwork.getOrElse(true))
|
|
||||||
.add(IsRandomTweetFeature, candidate.isRandomTweet.getOrElse(false))
|
|
||||||
.add(IsReadFromCacheFeature, candidate.isReadFromCache.getOrElse(false))
|
|
||||||
.add(IsRetweetFeature, candidate.isRetweet.getOrElse(false))
|
|
||||||
.add(IsRetweetedReplyFeature, isReply && isRetweet)
|
|
||||||
.add(
|
|
||||||
NonSelfFavoritedByUserIdsFeature,
|
|
||||||
candidate.favoritedByUserIds.getOrElse(Seq.empty).filterNot(_ == candidate.authorId))
|
|
||||||
.add(NumImagesFeature, numImages)
|
|
||||||
.add(
|
|
||||||
OriginalTweetCreationTimeFromSnowflakeFeature,
|
|
||||||
SnowflakeId.timeFromIdOpt(originalTweetId))
|
|
||||||
.add(PredictionRequestIdFeature, candidate.predictionRequestId)
|
|
||||||
.add(ScoreFeature, Some(candidate.score))
|
|
||||||
.add(
|
|
||||||
SimclustersTweetTopKClustersWithScoresFeature,
|
|
||||||
candidate.simclustersTweetTopKClustersWithScores.map(_.toMap).getOrElse(Map.empty))
|
|
||||||
.add(
|
|
||||||
StreamToKafkaFeature,
|
|
||||||
candidate.predictionRequestId.nonEmpty && candidate.fullScoringSucceeded.getOrElse(false))
|
|
||||||
.add(SourceTweetIdFeature, candidate.sourceTweetId)
|
|
||||||
.add(SourceUserIdFeature, candidate.sourceUserId)
|
|
||||||
.add(SuggestTypeFeature, suggestType)
|
|
||||||
.add(QuotedTweetIdFeature, candidate.quotedTweetId)
|
|
||||||
.add(
|
|
||||||
TweetLanguageFeature,
|
|
||||||
earlybirdFeatures.flatMap(_.language.map(_.value)).map(getLanguageISOFormatByValue))
|
|
||||||
.add(VideoDurationMsFeature, earlybirdFeatures.flatMap(_.videoDurationMs))
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,143 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.candidate_source.EarlybirdCandidateSource
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.DropMaxCandidatesFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.filter.PreviouslyServedTweetPreviewsFilter
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.TimelinesPersistenceStoreLastInjectionGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.AuthorEnabledPreviewsFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsHydratedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.PersistenceEntriesFeature
|
|
||||||
import com.twitter.home_mixer.product.for_you.feature_hydrator.AuthorEnabledPreviewsFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.for_you.feature_hydrator.TweetPreviewTweetypieCandidateFeatureHydrator
|
|
||||||
import com.twitter.home_mixer.product.for_you.filter.TweetPreviewTextFilter
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableTweetPreviewsCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.TweetPreviewsMaxCandidatesParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.TweetPreviewsMinInjectionIntervalParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.query_transformer.TweetPreviewsQueryTransformer
|
|
||||||
import com.twitter.home_mixer.product.for_you.response_transformer.TweetPreviewResponseFeatureTransformer
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.UrtItemCandidateDecorator
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.contextual_ref.ContextualTweetRefBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.item.tweet.TweetCandidateUrtItemBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.metadata.ClientEventInfoBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.feature_hydrator.query.social_graph.PreviewCreatorsFeature
|
|
||||||
import com.twitter.product_mixer.component_library.filter.FeatureFilter
|
|
||||||
import com.twitter.product_mixer.component_library.gate.NonEmptySeqFeatureGate
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.CandidateSourceWithExtractedFeatures
|
|
||||||
import com.twitter.product_mixer.core.functional_component.common.alert.Alert
|
|
||||||
import com.twitter.product_mixer.core.functional_component.decorator.CandidateDecorator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BaseCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.functional_component.filter.Filter
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.Gate
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidateFeatureTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineQueryTransformer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.transformer.CandidatePipelineResultsTransformer
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FilterIdentifier
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.rtf.safety_level.TimelineHomeTweetPreviewHydrationSafetyLevel
|
|
||||||
import com.twitter.product_mixer.core.model.marshalling.response.urt.contextual_ref.TweetHydrationContext
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.product_mixer.core.pipeline.candidate.CandidatePipelineConfig
|
|
||||||
import com.twitter.search.earlybird.{thriftscala => eb}
|
|
||||||
import com.twitter.timelines.configapi.FSParam
|
|
||||||
import com.twitter.timelines.injection.scribe.InjectionScribeUtil
|
|
||||||
import com.twitter.timelineservice.model.rich.EntityIdType
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => st}
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouTweetPreviewsCandidatePipelineConfig @Inject() (
|
|
||||||
earlybirdCandidateSource: EarlybirdCandidateSource,
|
|
||||||
authorEnabledPreviewsFeatureHydrator: AuthorEnabledPreviewsFeatureHydrator,
|
|
||||||
tweetPreviewsQueryTransformer: TweetPreviewsQueryTransformer,
|
|
||||||
tweetPreviewTweetypieCandidateFeatureHydrator: TweetPreviewTweetypieCandidateFeatureHydrator,
|
|
||||||
homeFeedbackActionInfoBuilder: HomeFeedbackActionInfoBuilder)
|
|
||||||
extends CandidatePipelineConfig[
|
|
||||||
ForYouQuery,
|
|
||||||
eb.EarlybirdRequest,
|
|
||||||
eb.ThriftSearchResult,
|
|
||||||
TweetCandidate
|
|
||||||
] {
|
|
||||||
|
|
||||||
val identifier: CandidatePipelineIdentifier = CandidatePipelineIdentifier("ForYouTweetPreviews")
|
|
||||||
|
|
||||||
override val supportedClientParam: Option[FSParam[Boolean]] =
|
|
||||||
Some(EnableTweetPreviewsCandidatePipelineParam)
|
|
||||||
|
|
||||||
override val gates: Seq[Gate[ForYouQuery]] = {
|
|
||||||
Seq(
|
|
||||||
TimelinesPersistenceStoreLastInjectionGate(
|
|
||||||
TweetPreviewsMinInjectionIntervalParam,
|
|
||||||
PersistenceEntriesFeature,
|
|
||||||
EntityIdType.TweetPreview
|
|
||||||
),
|
|
||||||
NonEmptySeqFeatureGate(PreviewCreatorsFeature)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val queryTransformer: CandidatePipelineQueryTransformer[
|
|
||||||
PipelineQuery,
|
|
||||||
eb.EarlybirdRequest
|
|
||||||
] = tweetPreviewsQueryTransformer
|
|
||||||
|
|
||||||
override val candidateSource: CandidateSourceWithExtractedFeatures[
|
|
||||||
eb.EarlybirdRequest,
|
|
||||||
eb.ThriftSearchResult
|
|
||||||
] = earlybirdCandidateSource
|
|
||||||
|
|
||||||
override val featuresFromCandidateSourceTransformers: Seq[
|
|
||||||
CandidateFeatureTransformer[eb.ThriftSearchResult]
|
|
||||||
] = Seq(TweetPreviewResponseFeatureTransformer)
|
|
||||||
|
|
||||||
override val resultTransformer: CandidatePipelineResultsTransformer[
|
|
||||||
eb.ThriftSearchResult,
|
|
||||||
TweetCandidate
|
|
||||||
] = { tweet => TweetCandidate(tweet.id) }
|
|
||||||
|
|
||||||
override val preFilterFeatureHydrationPhase1: Seq[
|
|
||||||
BaseCandidateFeatureHydrator[ForYouQuery, TweetCandidate, _]
|
|
||||||
] = Seq(
|
|
||||||
authorEnabledPreviewsFeatureHydrator,
|
|
||||||
tweetPreviewTweetypieCandidateFeatureHydrator,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val filters: Seq[
|
|
||||||
Filter[ForYouQuery, TweetCandidate]
|
|
||||||
] = Seq(
|
|
||||||
PreviouslyServedTweetPreviewsFilter,
|
|
||||||
FeatureFilter
|
|
||||||
.fromFeature(FilterIdentifier("TweetPreviewVisibilityFiltering"), IsHydratedFeature),
|
|
||||||
FeatureFilter
|
|
||||||
.fromFeature(FilterIdentifier("AuthorEnabledPreviews"), AuthorEnabledPreviewsFeature),
|
|
||||||
TweetPreviewTextFilter,
|
|
||||||
DropMaxCandidatesFilter(TweetPreviewsMaxCandidatesParam)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val decorator: Option[CandidateDecorator[PipelineQuery, TweetCandidate]] = {
|
|
||||||
val component = InjectionScribeUtil.scribeComponent(st.SuggestType.TweetPreview).get
|
|
||||||
val clientEventInfoBuilder = ClientEventInfoBuilder[PipelineQuery, TweetCandidate](component)
|
|
||||||
|
|
||||||
val tweetItemBuilder = TweetCandidateUrtItemBuilder[PipelineQuery, TweetCandidate](
|
|
||||||
clientEventInfoBuilder = clientEventInfoBuilder,
|
|
||||||
contextualTweetRefBuilder = Some(
|
|
||||||
ContextualTweetRefBuilder(
|
|
||||||
TweetHydrationContext(
|
|
||||||
safetyLevelOverride = Some(TimelineHomeTweetPreviewHydrationSafetyLevel),
|
|
||||||
outerTweetContext = None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
feedbackActionInfoBuilder = Some(homeFeedbackActionInfoBuilder),
|
|
||||||
)
|
|
||||||
|
|
||||||
Some(UrtItemCandidateDecorator(tweetItemBuilder))
|
|
||||||
}
|
|
||||||
|
|
||||||
override val alerts: Seq[Alert] = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(95))
|
|
||||||
}
|
|
Binary file not shown.
@ -1,56 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeWhoToFollowFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.DismissFatigueGate
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.TimelinesPersistenceStoreLastInjectionGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.DismissInfoFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.PersistenceEntriesFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.WhoToFollowExcludedUserIdsFeature
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableWhoToFollowCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToFollowDisplayLocationParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToFollowDisplayTypeIdParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToFollowMinInjectionIntervalParam
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.ParamWhoToFollowModuleDisplayTypeBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_follow_module.WhoToFollowArmCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.Gate
|
|
||||||
import com.twitter.timelineservice.model.rich.EntityIdType
|
|
||||||
import com.twitter.timelineservice.suggests.thriftscala.SuggestType
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouWhoToFollowCandidatePipelineConfigBuilder @Inject() (
|
|
||||||
whoToFollowArmCandidatePipelineConfigBuilder: WhoToFollowArmCandidatePipelineConfigBuilder,
|
|
||||||
homeWhoToFollowFeedbackActionInfoBuilder: HomeWhoToFollowFeedbackActionInfoBuilder) {
|
|
||||||
|
|
||||||
def build(): WhoToFollowArmCandidatePipelineConfig[ForYouQuery] = {
|
|
||||||
val gates: Seq[Gate[ForYouQuery]] = Seq(
|
|
||||||
TimelinesPersistenceStoreLastInjectionGate(
|
|
||||||
WhoToFollowMinInjectionIntervalParam,
|
|
||||||
PersistenceEntriesFeature,
|
|
||||||
EntityIdType.WhoToFollow
|
|
||||||
),
|
|
||||||
DismissFatigueGate(SuggestType.WhoToFollow, DismissInfoFeature)
|
|
||||||
)
|
|
||||||
|
|
||||||
whoToFollowArmCandidatePipelineConfigBuilder.build[ForYouQuery](
|
|
||||||
identifier = WhoToFollowArmCandidatePipelineConfig.identifier,
|
|
||||||
supportedClientParam = Some(EnableWhoToFollowCandidatePipelineParam),
|
|
||||||
alerts = alerts,
|
|
||||||
gates = gates,
|
|
||||||
moduleDisplayTypeBuilder =
|
|
||||||
ParamWhoToFollowModuleDisplayTypeBuilder(WhoToFollowDisplayTypeIdParam),
|
|
||||||
feedbackActionInfoBuilder = Some(homeWhoToFollowFeedbackActionInfoBuilder),
|
|
||||||
excludedUserIdsFeature = Some(WhoToFollowExcludedUserIdsFeature),
|
|
||||||
displayLocationParam = WhoToFollowDisplayLocationParam
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(70),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,52 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.functional_component.decorator.urt.builder.HomeWhoToSubscribeFeedbackActionInfoBuilder
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.DismissFatigueGate
|
|
||||||
import com.twitter.home_mixer.functional_component.gate.TimelinesPersistenceStoreLastInjectionGate
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.DismissInfoFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.PersistenceEntriesFeature
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.EnableWhoToSubscribeCandidatePipelineParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToSubscribeDisplayTypeIdParam
|
|
||||||
import com.twitter.home_mixer.product.for_you.param.ForYouParam.WhoToSubscribeMinInjectionIntervalParam
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.component_library.decorator.urt.builder.timeline_module.ParamWhoToFollowModuleDisplayTypeBuilder
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_subscribe_module.WhoToSubscribeCandidatePipelineConfig
|
|
||||||
import com.twitter.product_mixer.component_library.pipeline.candidate.who_to_subscribe_module.WhoToSubscribeCandidatePipelineConfigBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.gate.Gate
|
|
||||||
import com.twitter.timelineservice.model.rich.EntityIdType
|
|
||||||
import com.twitter.timelineservice.suggests.thriftscala.SuggestType
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ForYouWhoToSubscribeCandidatePipelineConfigBuilder @Inject() (
|
|
||||||
whoToSubscribeCandidatePipelineConfigBuilder: WhoToSubscribeCandidatePipelineConfigBuilder,
|
|
||||||
homeWhoToSubscribeFeedbackActionInfoBuilder: HomeWhoToSubscribeFeedbackActionInfoBuilder) {
|
|
||||||
|
|
||||||
def build(): WhoToSubscribeCandidatePipelineConfig[ForYouQuery] = {
|
|
||||||
val gates: Seq[Gate[ForYouQuery]] = Seq(
|
|
||||||
TimelinesPersistenceStoreLastInjectionGate(
|
|
||||||
WhoToSubscribeMinInjectionIntervalParam,
|
|
||||||
PersistenceEntriesFeature,
|
|
||||||
EntityIdType.WhoToSubscribe
|
|
||||||
),
|
|
||||||
DismissFatigueGate(SuggestType.WhoToSubscribe, DismissInfoFeature)
|
|
||||||
)
|
|
||||||
|
|
||||||
whoToSubscribeCandidatePipelineConfigBuilder.build[ForYouQuery](
|
|
||||||
identifier = WhoToSubscribeCandidatePipelineConfig.identifier,
|
|
||||||
supportedClientParam = Some(EnableWhoToSubscribeCandidatePipelineParam),
|
|
||||||
alerts = alerts,
|
|
||||||
gates = gates,
|
|
||||||
moduleDisplayTypeBuilder =
|
|
||||||
ParamWhoToFollowModuleDisplayTypeBuilder(WhoToSubscribeDisplayTypeIdParam),
|
|
||||||
feedbackActionInfoBuilder = Some(homeWhoToSubscribeFeedbackActionInfoBuilder)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(70),
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultEmptyResponseRateAlert()
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/javax/inject:javax.inject",
|
|
||||||
"finatra/inject/inject-core/src/main/scala",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model/request",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/model",
|
|
||||||
"home-mixer/thrift/src/main/thrift:thrift-scala",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/cursor",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/premarshaller/urt",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/candidate_source",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/candidate_source/product_pipeline",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline",
|
|
||||||
"src/thrift/com/twitter/search:earlybird-scala",
|
|
||||||
"stitch/stitch-timelineservice/src/main/scala",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/candidate_source",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,174 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you.candidate_source
|
|
||||||
|
|
||||||
import com.google.inject.Provider
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.ServedTweetIdsFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TimelineServiceTweetsFeature
|
|
||||||
import com.twitter.home_mixer.model.request.HomeMixerRequest
|
|
||||||
import com.twitter.home_mixer.model.request.ScoredTweetsProduct
|
|
||||||
import com.twitter.home_mixer.model.request.ScoredTweetsProductContext
|
|
||||||
import com.twitter.home_mixer.product.for_you.model.ForYouQuery
|
|
||||||
import com.twitter.home_mixer.{thriftscala => t}
|
|
||||||
import com.twitter.product_mixer.component_library.premarshaller.cursor.UrtCursorSerializer
|
|
||||||
import com.twitter.product_mixer.core.functional_component.candidate_source.product_pipeline.ProductPipelineCandidateSource
|
|
||||||
import com.twitter.product_mixer.core.functional_component.configapi.ParamsBuilder
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.CandidateSourceIdentifier
|
|
||||||
import com.twitter.product_mixer.core.product.registry.ProductPipelineRegistry
|
|
||||||
import com.twitter.timelines.render.{thriftscala => tl}
|
|
||||||
import com.twitter.timelineservice.suggests.{thriftscala => st}
|
|
||||||
import com.twitter.tweetconvosvc.tweet_ancestor.{thriftscala => ta}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [[ScoredTweetWithConversationMetadata]]
|
|
||||||
**/
|
|
||||||
case class ScoredTweetWithConversationMetadata(
|
|
||||||
tweetId: Long,
|
|
||||||
authorId: Long,
|
|
||||||
score: Option[Double] = None,
|
|
||||||
suggestType: Option[st.SuggestType] = None,
|
|
||||||
sourceTweetId: Option[Long] = None,
|
|
||||||
sourceUserId: Option[Long] = None,
|
|
||||||
quotedTweetId: Option[Long] = None,
|
|
||||||
quotedUserId: Option[Long] = None,
|
|
||||||
inReplyToTweetId: Option[Long] = None,
|
|
||||||
inReplyToUserId: Option[Long] = None,
|
|
||||||
directedAtUserId: Option[Long] = None,
|
|
||||||
inNetwork: Option[Boolean] = None,
|
|
||||||
sgsValidLikedByUserIds: Option[Seq[Long]] = None,
|
|
||||||
sgsValidFollowedByUserIds: Option[Seq[Long]] = None,
|
|
||||||
ancestors: Option[Seq[ta.TweetAncestor]] = None,
|
|
||||||
topicId: Option[Long] = None,
|
|
||||||
topicFunctionalityType: Option[tl.TopicContextFunctionalityType] = None,
|
|
||||||
conversationId: Option[Long] = None,
|
|
||||||
conversationFocalTweetId: Option[Long] = None,
|
|
||||||
isReadFromCache: Option[Boolean] = None,
|
|
||||||
streamToKafka: Option[Boolean] = None,
|
|
||||||
exclusiveConversationAuthorId: Option[Long] = None,
|
|
||||||
authorIsBlueVerified: Option[Boolean] = None,
|
|
||||||
authorIsGoldVerified: Option[Boolean] = None,
|
|
||||||
authorIsGrayVerified: Option[Boolean] = None,
|
|
||||||
authorIsLegacyVerified: Option[Boolean] = None,
|
|
||||||
authorIsCreator: Option[Boolean] = None,
|
|
||||||
perspectiveFilteredLikedByUserIds: Option[Seq[Long]] = None)
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ScoredTweetsProductCandidateSource @Inject() (
|
|
||||||
override val productPipelineRegistry: Provider[ProductPipelineRegistry],
|
|
||||||
override val paramsBuilder: Provider[ParamsBuilder])
|
|
||||||
extends ProductPipelineCandidateSource[
|
|
||||||
ForYouQuery,
|
|
||||||
HomeMixerRequest,
|
|
||||||
t.ScoredTweetsResponse,
|
|
||||||
ScoredTweetWithConversationMetadata
|
|
||||||
] {
|
|
||||||
|
|
||||||
override val identifier: CandidateSourceIdentifier =
|
|
||||||
CandidateSourceIdentifier("ScoredTweetsProduct")
|
|
||||||
|
|
||||||
private val MaxModuleSize = 3
|
|
||||||
private val MaxAncestorsInConversation = 2
|
|
||||||
|
|
||||||
override def pipelineRequestTransformer(productPipelineQuery: ForYouQuery): HomeMixerRequest = {
|
|
||||||
HomeMixerRequest(
|
|
||||||
clientContext = productPipelineQuery.clientContext,
|
|
||||||
product = ScoredTweetsProduct,
|
|
||||||
productContext = Some(
|
|
||||||
ScoredTweetsProductContext(
|
|
||||||
productPipelineQuery.deviceContext,
|
|
||||||
productPipelineQuery.seenTweetIds,
|
|
||||||
productPipelineQuery.features.map(_.getOrElse(ServedTweetIdsFeature, Seq.empty)),
|
|
||||||
productPipelineQuery.features.map(_.getOrElse(TimelineServiceTweetsFeature, Seq.empty))
|
|
||||||
)),
|
|
||||||
serializedRequestCursor =
|
|
||||||
productPipelineQuery.pipelineCursor.map(UrtCursorSerializer.serializeCursor),
|
|
||||||
maxResults = productPipelineQuery.requestedMaxResults,
|
|
||||||
debugParams = None,
|
|
||||||
homeRequestParam = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def productPipelineResultTransformer(
|
|
||||||
productPipelineResult: t.ScoredTweetsResponse
|
|
||||||
): Seq[ScoredTweetWithConversationMetadata] = {
|
|
||||||
val scoredTweets = productPipelineResult.scoredTweets.flatMap { focalTweet =>
|
|
||||||
val parentTweets = focalTweet.ancestors.getOrElse(Seq.empty).sortBy(-_.tweetId)
|
|
||||||
val (intermediates, root) = parentTweets.splitAt(parentTweets.size - 1)
|
|
||||||
val truncatedIntermediates =
|
|
||||||
intermediates.take(MaxModuleSize - MaxAncestorsInConversation).reverse
|
|
||||||
val rootScoredTweet: Seq[ScoredTweetWithConversationMetadata] = root.map { ancestor =>
|
|
||||||
ScoredTweetWithConversationMetadata(
|
|
||||||
tweetId = ancestor.tweetId,
|
|
||||||
authorId = ancestor.userId,
|
|
||||||
suggestType = focalTweet.suggestType,
|
|
||||||
conversationId = Some(ancestor.tweetId),
|
|
||||||
conversationFocalTweetId = Some(focalTweet.tweetId),
|
|
||||||
exclusiveConversationAuthorId = focalTweet.exclusiveConversationAuthorId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val conversationId = rootScoredTweet.headOption.map(_.tweetId)
|
|
||||||
|
|
||||||
val tweetsToParents =
|
|
||||||
if (parentTweets.nonEmpty) parentTweets.zip(parentTweets.tail).toMap
|
|
||||||
else Map.empty[ta.TweetAncestor, ta.TweetAncestor]
|
|
||||||
|
|
||||||
val intermediateScoredTweets = truncatedIntermediates.map { ancestor =>
|
|
||||||
ScoredTweetWithConversationMetadata(
|
|
||||||
tweetId = ancestor.tweetId,
|
|
||||||
authorId = ancestor.userId,
|
|
||||||
suggestType = focalTweet.suggestType,
|
|
||||||
inReplyToTweetId = tweetsToParents.get(ancestor).map(_.tweetId),
|
|
||||||
conversationId = conversationId,
|
|
||||||
conversationFocalTweetId = Some(focalTweet.tweetId),
|
|
||||||
exclusiveConversationAuthorId = focalTweet.exclusiveConversationAuthorId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val parentScoredTweets = rootScoredTweet ++ intermediateScoredTweets
|
|
||||||
|
|
||||||
val conversationFocalTweetId =
|
|
||||||
if (parentScoredTweets.nonEmpty) Some(focalTweet.tweetId) else None
|
|
||||||
|
|
||||||
val focalScoredTweet = ScoredTweetWithConversationMetadata(
|
|
||||||
tweetId = focalTweet.tweetId,
|
|
||||||
authorId = focalTweet.authorId,
|
|
||||||
score = focalTweet.score,
|
|
||||||
suggestType = focalTweet.suggestType,
|
|
||||||
sourceTweetId = focalTweet.sourceTweetId,
|
|
||||||
sourceUserId = focalTweet.sourceUserId,
|
|
||||||
quotedTweetId = focalTweet.quotedTweetId,
|
|
||||||
quotedUserId = focalTweet.quotedUserId,
|
|
||||||
inReplyToTweetId = parentScoredTweets.lastOption.map(_.tweetId),
|
|
||||||
inReplyToUserId = focalTweet.inReplyToUserId,
|
|
||||||
directedAtUserId = focalTweet.directedAtUserId,
|
|
||||||
inNetwork = focalTweet.inNetwork,
|
|
||||||
sgsValidLikedByUserIds = focalTweet.sgsValidLikedByUserIds,
|
|
||||||
sgsValidFollowedByUserIds = focalTweet.sgsValidFollowedByUserIds,
|
|
||||||
topicId = focalTweet.topicId,
|
|
||||||
topicFunctionalityType = focalTweet.topicFunctionalityType,
|
|
||||||
ancestors = focalTweet.ancestors,
|
|
||||||
conversationId = conversationId,
|
|
||||||
conversationFocalTweetId = conversationFocalTweetId,
|
|
||||||
isReadFromCache = focalTweet.isReadFromCache,
|
|
||||||
streamToKafka = focalTweet.streamToKafka,
|
|
||||||
exclusiveConversationAuthorId = focalTweet.exclusiveConversationAuthorId,
|
|
||||||
authorIsBlueVerified = focalTweet.authorMetadata.map(_.blueVerified),
|
|
||||||
authorIsGoldVerified = focalTweet.authorMetadata.map(_.goldVerified),
|
|
||||||
authorIsGrayVerified = focalTweet.authorMetadata.map(_.grayVerified),
|
|
||||||
authorIsLegacyVerified = focalTweet.authorMetadata.map(_.legacyVerified),
|
|
||||||
authorIsCreator = focalTweet.authorMetadata.map(_.creator),
|
|
||||||
perspectiveFilteredLikedByUserIds = focalTweet.perspectiveFilteredLikedByUserIds
|
|
||||||
)
|
|
||||||
|
|
||||||
parentScoredTweets :+ focalScoredTweet
|
|
||||||
}
|
|
||||||
|
|
||||||
val dedupedTweets = scoredTweets.groupBy(_.tweetId).map {
|
|
||||||
case (_, duplicateAncestors) => duplicateAncestors.maxBy(_.score.getOrElse(0.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by tweet id to prevent issues with future assumptions of the root being the first
|
|
||||||
// tweet and the focal being the last tweet in a module. The tweets as a whole do not need
|
|
||||||
// to be sorted overall, only the relative order within modules must be kept.
|
|
||||||
dedupedTweets.toSeq.sortBy(_.tweetId)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,72 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you.feature_hydrator
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.AuthorEnabledPreviewsFeature
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BulkCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.generated.client.audiencerewards.audienceRewardsService.GetCreatorPreferencesOnUserClientColumn
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hydrates the `AuthorEnabledPreviews` feature for tweets authored by creators by querying the
|
|
||||||
* `GetCreatorPreferences` Strato column. This feature corresponds to the `previews_enabled` field of that column.
|
|
||||||
* Given a tweet from a creator, this feature indicates whether that creator has enabled previews
|
|
||||||
* on their profile.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
class AuthorEnabledPreviewsFeatureHydrator @Inject() (
|
|
||||||
getCreatorPreferencesOnUserClientColumn: GetCreatorPreferencesOnUserClientColumn)
|
|
||||||
extends BulkCandidateFeatureHydrator[PipelineQuery, TweetCandidate] {
|
|
||||||
|
|
||||||
override val identifier: FeatureHydratorIdentifier =
|
|
||||||
FeatureHydratorIdentifier("AuthorEnabledPreviews")
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] = Set(AuthorEnabledPreviewsFeature)
|
|
||||||
|
|
||||||
private val fetcher = getCreatorPreferencesOnUserClientColumn.fetcher
|
|
||||||
|
|
||||||
private val DefaultAuthorEnabledPreviewsValue = true
|
|
||||||
|
|
||||||
override def apply(
|
|
||||||
query: PipelineQuery,
|
|
||||||
candidates: Seq[CandidateWithFeatures[TweetCandidate]]
|
|
||||||
): Stitch[Seq[FeatureMap]] = {
|
|
||||||
val candidateAuthors = candidates
|
|
||||||
.map(_.features.getOrElse(AuthorIdFeature, None))
|
|
||||||
.toSet
|
|
||||||
.flatten
|
|
||||||
|
|
||||||
// Build a map of creator -> authorEnabledPreviews, then use it to populate candidate features
|
|
||||||
val authorIdToFeatureStitch = Stitch.collect {
|
|
||||||
candidateAuthors
|
|
||||||
.map { author =>
|
|
||||||
val isAuthorEnabledPreviews = fetcher.fetch(author).map {
|
|
||||||
_.v.map(_.previewsEnabled).getOrElse(DefaultAuthorEnabledPreviewsValue)
|
|
||||||
}
|
|
||||||
(author, isAuthorEnabledPreviews)
|
|
||||||
}.toMap
|
|
||||||
}
|
|
||||||
|
|
||||||
authorIdToFeatureStitch.map { authorIdToFeatureMap =>
|
|
||||||
candidates.map {
|
|
||||||
_.features.getOrElse(AuthorIdFeature, None) match {
|
|
||||||
case Some(authorId) => FeatureMapBuilder()
|
|
||||||
.add(AuthorEnabledPreviewsFeature, authorIdToFeatureMap(authorId))
|
|
||||||
.build()
|
|
||||||
case _ => FeatureMapBuilder()
|
|
||||||
.add(AuthorEnabledPreviewsFeature, DefaultAuthorEnabledPreviewsValue)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"audience-rewards/thrift/src/main/thrift/common:thrift-scala",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/marshaller/timelines",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model/request",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/param",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/service",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/util",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/feature_hydrator/candidate/tweet_tweetypie",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/candidate",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/feature_hydrator",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/util",
|
|
||||||
"src/thrift/com/twitter/socialgraph:thrift-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"stitch/stitch-socialgraph",
|
|
||||||
"stitch/stitch-timelineservice",
|
|
||||||
"strato/config/columns/audiencerewards/audienceRewardsService:audienceRewardsService-strato-client",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/client",
|
|
||||||
"timelinemixer/common/src/main/scala/com/twitter/timelinemixer/clients/feedback",
|
|
||||||
"timelineservice/common/src/main/scala/com/twitter/timelineservice/model",
|
|
||||||
"util/util-core",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,84 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you.feature_hydrator
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.ConversationModuleFocalTweetIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.ConversationModuleIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.FocalTweetAuthorIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.FocalTweetInNetworkFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.FocalTweetRealNamesFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.FocalTweetScreenNamesFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.InNetworkFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.RealNamesFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.ScreenNamesFeature
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BulkCandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Social context for convo modules is hydrated on the root Tweet but needs info about the focal
|
|
||||||
* Tweet (e.g. author) to render the banner. This hydrator copies focal Tweet data into the root.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
class FocalTweetFeatureHydrator @Inject() ()
|
|
||||||
extends BulkCandidateFeatureHydrator[PipelineQuery, TweetCandidate] {
|
|
||||||
|
|
||||||
override val identifier: FeatureHydratorIdentifier = FeatureHydratorIdentifier("FocalTweet")
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] = Set(
|
|
||||||
FocalTweetAuthorIdFeature,
|
|
||||||
FocalTweetInNetworkFeature,
|
|
||||||
FocalTweetRealNamesFeature,
|
|
||||||
FocalTweetScreenNamesFeature
|
|
||||||
)
|
|
||||||
|
|
||||||
private val DefaultFeatureMap = FeatureMapBuilder()
|
|
||||||
.add(FocalTweetAuthorIdFeature, None)
|
|
||||||
.add(FocalTweetInNetworkFeature, None)
|
|
||||||
.add(FocalTweetRealNamesFeature, None)
|
|
||||||
.add(FocalTweetScreenNamesFeature, None)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override def apply(
|
|
||||||
query: PipelineQuery,
|
|
||||||
candidates: Seq[CandidateWithFeatures[TweetCandidate]]
|
|
||||||
): Stitch[Seq[FeatureMap]] = {
|
|
||||||
// Build a map of all the focal tweets to their corresponding features
|
|
||||||
val focalTweetIdToFeatureMap = candidates.flatMap { candidate =>
|
|
||||||
val focalTweetId = candidate.features.getOrElse(ConversationModuleFocalTweetIdFeature, None)
|
|
||||||
if (focalTweetId.contains(candidate.candidate.id)) {
|
|
||||||
Some(candidate.candidate.id -> candidate.features)
|
|
||||||
} else None
|
|
||||||
}.toMap
|
|
||||||
|
|
||||||
val updatedFeatureMap = candidates.map { candidate =>
|
|
||||||
val focalTweetId = candidate.features.getOrElse(ConversationModuleFocalTweetIdFeature, None)
|
|
||||||
val conversationId = candidate.features.getOrElse(ConversationModuleIdFeature, None)
|
|
||||||
|
|
||||||
// Check if the candidate is a root tweet and ensure its focal tweet's features are available
|
|
||||||
if (conversationId.contains(candidate.candidate.id)
|
|
||||||
&& focalTweetId.exists(focalTweetIdToFeatureMap.contains)) {
|
|
||||||
val featureMap = focalTweetIdToFeatureMap.get(focalTweetId.get).get
|
|
||||||
FeatureMapBuilder()
|
|
||||||
.add(FocalTweetAuthorIdFeature, featureMap.getOrElse(AuthorIdFeature, None))
|
|
||||||
.add(FocalTweetInNetworkFeature, Some(featureMap.getOrElse(InNetworkFeature, true)))
|
|
||||||
.add(
|
|
||||||
FocalTweetRealNamesFeature,
|
|
||||||
Some(featureMap.getOrElse(RealNamesFeature, Map.empty[Long, String])))
|
|
||||||
.add(
|
|
||||||
FocalTweetScreenNamesFeature,
|
|
||||||
Some(featureMap.getOrElse(ScreenNamesFeature, Map.empty[Long, String])))
|
|
||||||
.build()
|
|
||||||
} else DefaultFeatureMap
|
|
||||||
}
|
|
||||||
|
|
||||||
Stitch.value(updatedFeatureMap)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,62 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you.feature_hydrator
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.marshaller.timelines.DeviceContextMarshaller
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TimelineServiceTweetsFeature
|
|
||||||
import com.twitter.home_mixer.model.request.DeviceContext
|
|
||||||
import com.twitter.home_mixer.model.request.HasDeviceContext
|
|
||||||
import com.twitter.home_mixer.service.HomeMixerAlertConfig
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.QueryFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.stitch.timelineservice.TimelineService
|
|
||||||
import com.twitter.timelineservice.{thriftscala => t}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
case class TimelineServiceTweetsQueryFeatureHydrator @Inject() (
|
|
||||||
timelineService: TimelineService,
|
|
||||||
deviceContextMarshaller: DeviceContextMarshaller)
|
|
||||||
extends QueryFeatureHydrator[PipelineQuery with HasDeviceContext] {
|
|
||||||
|
|
||||||
override val identifier: FeatureHydratorIdentifier =
|
|
||||||
FeatureHydratorIdentifier("TimelineServiceTweets")
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] = Set(TimelineServiceTweetsFeature)
|
|
||||||
|
|
||||||
private val MaxTimelineServiceTweets = 200
|
|
||||||
|
|
||||||
override def hydrate(query: PipelineQuery with HasDeviceContext): Stitch[FeatureMap] = {
|
|
||||||
val deviceContext = query.deviceContext.getOrElse(DeviceContext.Empty)
|
|
||||||
|
|
||||||
val timelineQueryOptions = t.TimelineQueryOptions(
|
|
||||||
contextualUserId = query.clientContext.userId,
|
|
||||||
deviceContext = Some(deviceContextMarshaller(deviceContext, query.clientContext))
|
|
||||||
)
|
|
||||||
|
|
||||||
val timelineServiceQuery = t.TimelineQuery(
|
|
||||||
timelineType = t.TimelineType.Home,
|
|
||||||
timelineId = query.getRequiredUserId,
|
|
||||||
maxCount = MaxTimelineServiceTweets.toShort,
|
|
||||||
cursor2 = None,
|
|
||||||
options = Some(timelineQueryOptions),
|
|
||||||
timelineId2 = query.clientContext.userId.map(t.TimelineId(t.TimelineType.Home, _, None)),
|
|
||||||
)
|
|
||||||
|
|
||||||
timelineService.getTimeline(timelineServiceQuery).map { timeline =>
|
|
||||||
val tweets = timeline.entries.collect {
|
|
||||||
case t.TimelineEntry.Tweet(tweet) => tweet.statusId
|
|
||||||
}
|
|
||||||
|
|
||||||
FeatureMapBuilder().add(TimelineServiceTweetsFeature, tweets).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val alerts = Seq(
|
|
||||||
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(99.7)
|
|
||||||
)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,72 +0,0 @@
|
|||||||
package com.twitter.home_mixer.product.for_you.feature_hydrator
|
|
||||||
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.IsHydratedFeature
|
|
||||||
import com.twitter.home_mixer.model.HomeFeatures.TweetTextFeature
|
|
||||||
import com.twitter.product_mixer.component_library.model.candidate.BaseTweetCandidate
|
|
||||||
import com.twitter.product_mixer.core.feature.Feature
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
||||||
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
||||||
import com.twitter.product_mixer.core.functional_component.feature_hydrator.CandidateFeatureHydrator
|
|
||||||
import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier
|
|
||||||
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
||||||
import com.twitter.spam.rtf.{thriftscala => rtf}
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.stitch.tweetypie.{TweetyPie => TweetypieStitchClient}
|
|
||||||
import com.twitter.tweetypie.{thriftscala => TP}
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class TweetPreviewTweetypieCandidateFeatureHydrator @Inject() (
|
|
||||||
tweetypieStitchClient: TweetypieStitchClient)
|
|
||||||
extends CandidateFeatureHydrator[PipelineQuery, BaseTweetCandidate] {
|
|
||||||
|
|
||||||
private val CoreTweetFields: Set[TP.TweetInclude] = Set[TP.TweetInclude](
|
|
||||||
TP.TweetInclude.TweetFieldId(TP.Tweet.IdField.id),
|
|
||||||
TP.TweetInclude.TweetFieldId(TP.Tweet.CoreDataField.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val DefaultFeatureMap = FeatureMapBuilder()
|
|
||||||
.add(TweetTextFeature, None)
|
|
||||||
.add(IsHydratedFeature, false)
|
|
||||||
.add(AuthorIdFeature, None)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override val features: Set[Feature[_, _]] =
|
|
||||||
Set(TweetTextFeature, IsHydratedFeature, AuthorIdFeature)
|
|
||||||
|
|
||||||
override val identifier: FeatureHydratorIdentifier =
|
|
||||||
FeatureHydratorIdentifier("TweetPreviewTweetypie")
|
|
||||||
|
|
||||||
override def apply(
|
|
||||||
query: PipelineQuery,
|
|
||||||
candidate: BaseTweetCandidate,
|
|
||||||
existingFeatures: FeatureMap
|
|
||||||
): Stitch[FeatureMap] = {
|
|
||||||
tweetypieStitchClient
|
|
||||||
.getTweetFields(
|
|
||||||
tweetId = candidate.id,
|
|
||||||
options = TP.GetTweetFieldsOptions(
|
|
||||||
tweetIncludes = CoreTweetFields,
|
|
||||||
includeRetweetedTweet = false,
|
|
||||||
includeQuotedTweet = false,
|
|
||||||
visibilityPolicy = TP.TweetVisibilityPolicy.UserVisible,
|
|
||||||
safetyLevel = Some(rtf.SafetyLevel.TimelineHomeTweetPreview),
|
|
||||||
forUserId = query.getOptionalUserId
|
|
||||||
)
|
|
||||||
).map {
|
|
||||||
case TP.GetTweetFieldsResult(_, TP.TweetFieldsResultState.Found(found), quoteOpt, _) =>
|
|
||||||
val tweetText = found.tweet.coreData.map(_.text)
|
|
||||||
FeatureMapBuilder()
|
|
||||||
.add(TweetTextFeature, tweetText)
|
|
||||||
.add(IsHydratedFeature, true)
|
|
||||||
.add(AuthorIdFeature, found.tweet.coreData.map(_.userId))
|
|
||||||
.build()
|
|
||||||
// If no tweet result found, return default features
|
|
||||||
case _ =>
|
|
||||||
DefaultFeatureMap
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/model",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/product/for_you/param",
|
|
||||||
"home-mixer/server/src/main/scala/com/twitter/home_mixer/util",
|
|
||||||
"product-mixer/component-library/src/main/scala/com/twitter/product_mixer/component_library/model/candidate",
|
|
||||||
"product-mixer/core/src/main/scala/com/twitter/product_mixer/core/functional_component/filter",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"timelineservice/common/src/main/scala/com/twitter/timelineservice/model",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user