in visibilitylib/src/main/scala/com/twitter/visibility/interfaces/conversations/TimelineConversationsVisibilityLibrary.scala [47:233]
def apply(
visibilityLibrary: VisibilityLibrary,
batchSafetyLabelRepository: BatchSafetyLabelRepository,
decider: Decider,
userRelationshipSource: UserRelationshipSource = UserRelationshipSource.empty,
userSource: UserSource = UserSource.empty
): Type = {
val libraryStatsReceiver = visibilityLibrary.statsReceiver
val tweetIdFeatures = new TweetIdFeatures(
statsReceiver = libraryStatsReceiver,
enableStitchProfiling = Gate.False
)
val tweetIdFeaturesMinimal = new TweetIdFeatures(
statsReceiver = libraryStatsReceiver,
enableStitchProfiling = Gate.False
)
val vfLatencyOverallStat = libraryStatsReceiver.stat("vf_latency_overall")
val vfLatencyStitchBuildStat = libraryStatsReceiver.stat("vf_latency_stitch_build")
val vfLatencyStitchRunStat = libraryStatsReceiver.stat("vf_latency_stitch_run")
val visibilityDeciderGates = VisibilityDeciderGates(decider)
val verdictLogger =
createVerdictLogger(
visibilityDeciderGates.enableVerdictLoggerTCVL,
decider,
libraryStatsReceiver)
request: TimelineConversationsVisibilityRequest =>
val elapsed = Stopwatch.start()
var runStitchStartMs = 0L
val future = request.prefetchedSafetyLabels match {
case Some(labels) => Future.value(labels)
case _ =>
batchSafetyLabelRepository((request.conversationId, request.tweetIds))
}
val fosnrPefetchedLabelsRelationshipFeatures =
new FosnrPefetchedLabelsRelationshipFeatures(
userRelationshipSource = userRelationshipSource,
statsReceiver = libraryStatsReceiver)
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
Stitch.callFuture(future).flatMap {
kvr: KeyValueResult[Long, scala.collection.Map[SafetyLabelType, SafetyLabel]] =>
val featureMapProvider: (ContentId, SafetyLevel) => FeatureMap = {
case (TweetId(tweetId), safetyLevel) =>
val constantTweetSafetyLabels: Seq[TweetSafetyLabel] =
kvr.found.getOrElse(tweetId, Map.empty).toSeq.map {
case (safetyLabelType, safetyLabel) =>
TweetSafetyLabel.fromThrift(SafetyLabelValue(safetyLabelType, safetyLabel))
}
val replyAuthor = request.tweetAuthors.flatMap {
_(tweetId) match {
case Return(Some(userId)) => Some(userId)
case _ => None
}
}
val fosnrPefetchedLabelsRelationshipFeatureConf = replyAuthor match {
case Some(authorId) if visibilityLibrary.isReleaseCandidateEnabled =>
fosnrPefetchedLabelsRelationshipFeatures
.forTweetWithSafetyLabelsAndAuthorId(
safetyLabels = constantTweetSafetyLabels,
authorId = authorId,
viewerId = request.viewerContext.userId)
case _ => fosnrPefetchedLabelsRelationshipFeatures.forNonFosnr()
}
val authorFeatureConf = replyAuthor match {
case Some(authorId) if visibilityLibrary.isReleaseCandidateEnabled =>
authorFeatures.forAuthorId(authorId)
case _ => authorFeatures.forNoAuthor()
}
val baseBuilderArguments = (safetyLevel match {
case TimelineConversationsDownranking =>
Seq(tweetIdFeatures.forTweetId(tweetId, constantTweetSafetyLabels))
case TimelineConversationsDownrankingMinimal =>
Seq(tweetIdFeaturesMinimal.forTweetId(tweetId, constantTweetSafetyLabels))
case _ => Nil
}) :+ fosnrPefetchedLabelsRelationshipFeatureConf :+ authorFeatureConf
val tweetAuthorUserLabels: Option[Seq[Label]] =
request.prefetchedTweetAuthorUserLabels.flatMap {
_.apply(tweetId) match {
case Return(Some(labelMap)) =>
Some(labelMap.values.toSeq)
case _ =>
None
}
}
val hasInnerCircleOfFriendsRelationship: Boolean =
request.innerCircleOfFriendsRelationships match {
case Some(keyValueResult) =>
keyValueResult(tweetId) match {
case Return(Some(true)) => true
case _ => false
}
case None => false
}
val builderArguments: Seq[FeatureMapBuilder => FeatureMapBuilder] =
tweetAuthorUserLabels match {
case Some(labels) =>
baseBuilderArguments :+ { (fmb: FeatureMapBuilder) =>
fmb.withConstantFeature(AuthorUserLabels, labels)
}
case None =>
baseBuilderArguments :+ { (fmb: FeatureMapBuilder) =>
fmb.withConstantFeature(AuthorUserLabels, Seq.empty)
}
case _ =>
baseBuilderArguments
}
val tweetParentIdOpt: Option[Long] =
request.tweetParentIdMap.flatMap(tweetParentIdMap => tweetParentIdMap(tweetId))
visibilityLibrary.featureMapBuilder(builderArguments :+ { (fmb: FeatureMapBuilder) =>
fmb.withConstantFeature(
HasInnerCircleOfFriendsRelationship,
hasInnerCircleOfFriendsRelationship)
fmb.withConstantFeature(TweetConversationId, request.conversationId)
fmb.withConstantFeature(TweetParentId, tweetParentIdOpt)
fmb.withConstantFeature(
ConversationRootAuthorIsVerified,
request.rootAuthorIsVerified)
})
case _ =>
visibilityLibrary.featureMapBuilder(Nil)
}
val safetyLevel =
if (request.minimalSectioningOnly) TimelineConversationsDownrankingMinimal
else TimelineConversationsDownranking
val evaluationContextBuilder = visibilityLibrary
.evaluationContextBuilder(request.viewerContext)
.withUnitOfDiversion(UnitOfDiversion.ConversationId(request.conversationId))
visibilityLibrary
.runRuleEngineBatch(
request.tweetIds.map(TweetId),
featureMapProvider,
evaluationContextBuilder,
safetyLevel
)
.map { results: Seq[Try[VisibilityResult]] =>
val (succeededRequests, _) = results.partition(_.exists(_.finished))
val visibilityResultMap = succeededRequests.flatMap {
case Return(result) =>
scribeVisibilityVerdict(
result,
visibilityDeciderGates.enableVerdictScribingTCVL,
verdictLogger,
request.viewerContext.userId,
safetyLevel)
result.contentId match {
case TweetId(id) => Some((id, result))
case _ => None
}
case _ => None
}.toMap
val failedTweetIds = request.tweetIds diff visibilityResultMap.keys.toSeq
val response = TimelineConversationsVisibilityResponse(
visibilityResults = visibilityResultMap,
failedTweetIds = failedTweetIds
)
runStitchStartMs = elapsed().inMilliseconds
val buildStitchStatMs = elapsed().inMilliseconds
vfLatencyStitchBuildStat.add(buildStitchStatMs)
response
}
.onSuccess(_ => {
val overallStatMs = elapsed().inMilliseconds
vfLatencyOverallStat.add(overallStatMs)
val runStitchEndMs = elapsed().inMilliseconds
vfLatencyStitchRunStat.add(runStitchEndMs - runStitchStartMs)
})
}
}