[docx] split commit for file 1600

Signed-off-by: Ari Archer <ari.web.xyz@gmail.com>
This commit is contained in:
Ari Archer 2024-01-23 19:07:56 +02:00
parent e27b2e31c3
commit 94812b2b2c
No known key found for this signature in database
GPG Key ID: A50D5B4B599AF8A2
400 changed files with 0 additions and 14397 deletions

View File

@ -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
)
}
}

View File

@ -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",
],
)

View File

@ -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]
}
}

View File

@ -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
)
}

View File

@ -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
)
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -1,10 +0,0 @@
scala_library(
sources = ["*.scala"],
compiler_option_sets = ["fatal_warnings"],
platform = "java8",
strict_deps = True,
tags = ["bazel-compatible"],
dependencies = [
"servo/decider",
],
)

View File

@ -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")
}

View File

@ -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",
],
)

View File

@ -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]
}
}

View File

@ -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,
)
}

View File

@ -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",
],
)

View File

@ -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),
)
}

View File

@ -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) }
}

View File

@ -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),
)
}
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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()
)
}

View File

@ -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",
],
)

View File

@ -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
}

View File

@ -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")
}

View File

@ -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",
],
)

View File

@ -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
)
}

View File

@ -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)
}

View File

@ -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",
],
)

View File

@ -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),
)
}

View File

@ -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)
)
}

View File

@ -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()
)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}
}

View File

@ -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)
)
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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)
)
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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))
}

View File

@ -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()
)
}

View File

@ -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()
)
}

View File

@ -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",
],
)

View File

@ -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)
}
}

View File

@ -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()
}
}
}
}
}

View File

@ -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",
],
)

View File

@ -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)
}
}

View File

@ -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)
)
}

View File

@ -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
}
}
}

View File

@ -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",
],
)

Some files were not shown because too many files have changed in this diff Show More