diff --git a/src/protocol/commands.proto b/src/protocol/commands.proto index d9ca8c819..6886d7682 100644 --- a/src/protocol/commands.proto +++ b/src/protocol/commands.proto @@ -543,7 +543,7 @@ message Capability { [default = NO_TEXT_DELETION_CAPABILITY]; } -// Next ID: 105 +// Next ID: 106 // Bundles together some Android experiment flags so that they can be easily // retrieved throughout the native code. These flags are generally specific to // the decoder, and are made available when the decoder is initialized. @@ -646,6 +646,9 @@ message DecoderExperimentParams { // history rewriter wheh the target segment contains proper noun candidate. optional bool user_segment_history_rewriter_replace_proper_noun = 103 [default = false]; + + // Apply inner segment boundary information to the single segment candidate. + optional bool apply_single_inner_segment_boundary = 105 [default = true]; } // Clients' request to the server. diff --git a/src/rewriter/user_segment_history_rewriter.cc b/src/rewriter/user_segment_history_rewriter.cc index d54b3c57d..d38058c3d 100644 --- a/src/rewriter/user_segment_history_rewriter.cc +++ b/src/rewriter/user_segment_history_rewriter.cc @@ -740,11 +740,21 @@ bool UserSegmentHistoryRewriter::IsAvailable(const ConversionRequest &request, // Returns segments for learning. // Inner segments boundary will be expanded. Segments UserSegmentHistoryRewriter::MakeLearningSegmentsFromInnerSegments( - const Segments &segments) { + const ConversionRequest &request, const Segments &segments) { + auto inner_segments_info_available = [&request](const Segment::Candidate &c) { + if (request.request() + .decoder_experiment_params() + .apply_single_inner_segment_boundary()) { + return !c.inner_segment_boundary.empty(); + } else { + return c.inner_segment_boundary.size() > 1; + } + }; + Segments ret; for (const Segment &segment : segments) { const Segment::Candidate &candidate = segment.candidate(0); - if (candidate.inner_segment_boundary.empty()) { + if (!inner_segments_info_available(candidate)) { // No inner segment info Segment *seg = ret.add_segment(); *seg = segment; @@ -796,7 +806,7 @@ void UserSegmentHistoryRewriter::Finish(const ConversionRequest &request, const Segments target_segments = UseInnerSegments(request) - ? MakeLearningSegmentsFromInnerSegments(*segments) + ? MakeLearningSegmentsFromInnerSegments(request, *segments) : *segments; std::vector revert_entries; for (size_t i = target_segments.history_segments_size(); diff --git a/src/rewriter/user_segment_history_rewriter.h b/src/rewriter/user_segment_history_rewriter.h index c9e3a0c98..6187bb064 100644 --- a/src/rewriter/user_segment_history_rewriter.h +++ b/src/rewriter/user_segment_history_rewriter.h @@ -90,7 +90,7 @@ class UserSegmentHistoryRewriter : public RewriterInterface { }; static Segments MakeLearningSegmentsFromInnerSegments( - const Segments &segments); + const ConversionRequest &request, const Segments &segments); // Returns id for RevertEntry static uint16_t revert_id(); diff --git a/src/rewriter/user_segment_history_rewriter_test.cc b/src/rewriter/user_segment_history_rewriter_test.cc index 4b331a4db..1262332fa 100644 --- a/src/rewriter/user_segment_history_rewriter_test.cc +++ b/src/rewriter/user_segment_history_rewriter_test.cc @@ -67,9 +67,9 @@ class UserSegmentHistoryRewriterTestPeer { UserSegmentHistoryRewriterTestPeer() = delete; static Segments MakeLearningSegmentsFromInnerSegments( - const Segments &segments) { + const ConversionRequest &request, const Segments &segments) { return UserSegmentHistoryRewriter::MakeLearningSegmentsFromInnerSegments( - segments); + request, segments); } }; @@ -1755,9 +1755,11 @@ TEST_F(UserSegmentHistoryRewriterTest, SupportInnerSegmentsOnLearning) { Segment::Candidate::RERANKED; segments.mutable_segment(0)->set_segment_type(Segment::FIXED_VALUE); + const ConversionRequest default_mobile_convreq = CreateConversionRequest(); { const Segments learning_segments = UserSegmentHistoryRewriterTestPeer:: - MakeLearningSegmentsFromInnerSegments(segments); + MakeLearningSegmentsFromInnerSegments(default_mobile_convreq, + segments); EXPECT_EQ(learning_segments.segments_size(), 3); EXPECT_EQ(learning_segments.segment(0).key(), "わたしの"); EXPECT_EQ(learning_segments.segment(0).candidate(0).key, "わたしの"); @@ -1795,8 +1797,7 @@ TEST_F(UserSegmentHistoryRewriterTest, SupportInnerSegmentsOnLearning) { Segment::FIXED_VALUE); } - const ConversionRequest convreq = CreateConversionRequest(); - rewriter->Finish(convreq, &segments); + rewriter->Finish(default_mobile_convreq, &segments); } { @@ -1823,9 +1824,11 @@ TEST_F(UserSegmentHistoryRewriterTest, SupportInnerSegmentsOnLearning) { Segment::Candidate::RERANKED; segments.mutable_segment(0)->set_segment_type(Segment::FIXED_VALUE); + const ConversionRequest default_mobile_convreq = CreateConversionRequest(); { const Segments learning_segments = UserSegmentHistoryRewriterTestPeer:: - MakeLearningSegmentsFromInnerSegments(segments); + MakeLearningSegmentsFromInnerSegments(default_mobile_convreq, + segments); EXPECT_EQ(learning_segments.segments_size(), 1); EXPECT_EQ(learning_segments.segment(0).key(), "わたしの"); EXPECT_EQ(learning_segments.segment(0).candidate(0).key, "わたしの"); @@ -1839,52 +1842,7 @@ TEST_F(UserSegmentHistoryRewriterTest, SupportInnerSegmentsOnLearning) { Segment::FIXED_VALUE); } - const ConversionRequest convreq = CreateConversionRequest(); - rewriter->Finish(convreq, &segments); - } - - { - // Inner segment boundary with size 1 may have better information. - segments.Clear(); - InitSegments(&segments, 1, 2); - constexpr absl::string_view kKey = "わたしの"; - constexpr absl::string_view kValue = "私の"; - segments.mutable_segment(0)->set_key(kKey); - Segment::Candidate *candidate = - segments.mutable_segment(0)->mutable_candidate(1); - - candidate->value = kValue; - candidate->content_value = kValue; - candidate->key = kKey; - candidate->content_key = kKey; - // "わたしの, 私の", "わたし, 私" - candidate->PushBackInnerSegmentBoundary(12, 6, 9, 3); - candidate->lid = 10; - candidate->rid = 10; - - segments.mutable_segment(0)->move_candidate(1, 0); - segments.mutable_segment(0)->mutable_candidate(0)->attributes |= - Segment::Candidate::RERANKED; - segments.mutable_segment(0)->set_segment_type(Segment::FIXED_VALUE); - - { - const Segments learning_segments = UserSegmentHistoryRewriterTestPeer:: - MakeLearningSegmentsFromInnerSegments(segments); - EXPECT_EQ(learning_segments.segments_size(), 1); - EXPECT_EQ(learning_segments.segment(0).key(), "わたしの"); - EXPECT_EQ(learning_segments.segment(0).candidate(0).key, "わたしの"); - EXPECT_EQ(learning_segments.segment(0).candidate(0).value, "私の"); - EXPECT_EQ(learning_segments.segment(0).candidate(0).content_key, - "わたし"); - EXPECT_EQ(learning_segments.segment(0).candidate(0).content_value, "私"); - EXPECT_EQ(learning_segments.segment(0).candidate(0).lid, 10); - EXPECT_EQ(learning_segments.segment(0).candidate(0).rid, 10); - EXPECT_EQ(learning_segments.segment(0).segment_type(), - Segment::FIXED_VALUE); - } - - const ConversionRequest convreq = CreateConversionRequest(); - rewriter->Finish(convreq, &segments); + rewriter->Finish(default_mobile_convreq, &segments); } { @@ -1904,10 +1862,59 @@ TEST_F(UserSegmentHistoryRewriterTest, SupportInnerSegmentsOnLearning) { candidate->content_key = "なかの"; candidate->content_key = "なかの"; - const ConversionRequest convreq = CreateConversionRequest(); - EXPECT_TRUE(rewriter->Rewrite(convreq, &segments)); + const ConversionRequest default_mobile_convreq = CreateConversionRequest(); + EXPECT_TRUE(rewriter->Rewrite(default_mobile_convreq, &segments)); EXPECT_EQ(segments.segment(0).candidate(0).value, "中野"); } + + { + // Disable inner segment boundary for single segment + request_->mutable_decoder_experiment_params() + ->set_apply_single_inner_segment_boundary(false); + + // Inner segment boundary with size 1 may have better information. + segments.Clear(); + InitSegments(&segments, 1, 2); + constexpr absl::string_view kKey = "わたしの"; + constexpr absl::string_view kValue = "私の"; + segments.mutable_segment(0)->set_key(kKey); + Segment::Candidate *candidate = + segments.mutable_segment(0)->mutable_candidate(1); + + candidate->value = kValue; + candidate->content_value = kValue; + candidate->key = kKey; + candidate->content_key = kKey; + // "わたしの, 私の", "わたし, 私" + candidate->PushBackInnerSegmentBoundary(12, 6, 9, 3); + candidate->lid = 10; + candidate->rid = 10; + + segments.mutable_segment(0)->move_candidate(1, 0); + segments.mutable_segment(0)->mutable_candidate(0)->attributes |= + Segment::Candidate::RERANKED; + segments.mutable_segment(0)->set_segment_type(Segment::FIXED_VALUE); + + const ConversionRequest convreq = CreateConversionRequest(); + { + const Segments learning_segments = UserSegmentHistoryRewriterTestPeer:: + MakeLearningSegmentsFromInnerSegments(convreq, segments); + EXPECT_EQ(learning_segments.segments_size(), 1); + EXPECT_EQ(learning_segments.segment(0).key(), "わたしの"); + EXPECT_EQ(learning_segments.segment(0).candidate(0).key, "わたしの"); + EXPECT_EQ(learning_segments.segment(0).candidate(0).value, "私の"); + EXPECT_EQ(learning_segments.segment(0).candidate(0).content_key, + "わたしの"); + EXPECT_EQ(learning_segments.segment(0).candidate(0).content_value, + "私の"); + EXPECT_EQ(learning_segments.segment(0).candidate(0).lid, 10); + EXPECT_EQ(learning_segments.segment(0).candidate(0).rid, 10); + EXPECT_EQ(learning_segments.segment(0).segment_type(), + Segment::FIXED_VALUE); + } + + rewriter->Finish(convreq, &segments); + } } TEST_F(UserSegmentHistoryRewriterTest, ReplaceableSingleKanji) {