Merge pull request #453 from fanlusky/fix/online-candidate-manual-segmentation

fix(engine): 云/AI 联想候选与全拼云查询遵循手动分词
This commit is contained in:
Lu Fan(SonnyCalcr) authored and GitHub committed 2026-09-22 01:15:51 +08:00
commit 56bfc046d5
3 files changed
+52 -12

No files matched your search

+7 -2
View File
@@ -721,8 +721,13 @@ InputSession::CloudQueryState InputSession::get_cloud_query_state() const
}
state.should_query = !request().normalized_input.empty();
// 全拼敲出来的 nve / lve 同样要换写法;没有音节边界的裸串匹配不上,原样透传。
state.query_text = quanpin::to_google_spelling(request().normalized_input);
// 云端要按用户看到的分词来查:手动把 qi'e'huan 分成三段,就该带着音节边界发给云接口,
// 否则云端自行贪心断句会当成 qie'huan(切换)。normalized_segmentation 保留了用户的手动
// 撇号(cut_pinyin_with_corrections 先按撇号切分),既定住了分词,也让 to_google_spelling
// 能逐音节把 nve / lve 换成云端认的 nue / lue 写法。分词缺失时退回裸串。
const std::string &segmentation =
request().normalized_segmentation.empty() ? request().normalized_input : request().normalized_segmentation;
state.query_text = quanpin::to_google_spelling(segmentation);
return state;
}
+4 -4
View File
@@ -915,10 +915,10 @@ int QuanpinDictionary::create_word_from_canonical_pinyin(std::string pinyin, std
pinyin = quanpin::join_segments(segments);
const std::string jp = quanpin::segments_to_jianpin(segments);
if (!do_validate(pinyin, jp, word))
{
return ERROR_CODE;
}
// 不再走 do_validate:这里的音节边界由调用方(canonical 键)显式给定,上面已按
// split_segments 校验过段数、逐段完整性,jp 也是每段一字母、长度必然等于字数。
// do_validate 会先 remove_delimiters 再用贪心 correction 重切,把 qi'e'huan 断成
// qie'huan(段数 3→2),从而误杀这条合法整句——正是 AI / 云联想选中却入不了库的根因。
if (check_data(build_sql_for_checking_word(pinyin, word)))
{
return OK;
+41 -6
View File
@@ -4798,20 +4798,55 @@ void ProcessSelectionKey(UINT keycode, uint64_t client_id, uint64_t activation_e
return;
}
std::string cloudCommittedPinyin;
bool cloudCommittedPinyinIsCanonical = false;
std::string aiCommittedPinyin;
bool aiCommittedPinyinIsCanonical = false;
if (curWordItem.source == CandidateSource::CloudSuggestion)
{
cloudCommittedPinyin = g_inputSession->get_cloud_query_state().committed_pinyin;
if (!curWordItem.canonical_pinyin.empty())
{
cloudCommittedPinyin = curWordItem.canonical_pinyin;
cloudCommittedPinyinIsCanonical = true;
}
else if (g_inputSession->is_all_complete_pure_pinyin())
{
// 与 AI 联想同一个坑:committed_pinyin 走的是 normalized_input,丢掉了音节
// 边界,create_word 会用贪心的 "correction" 切分重新断句,qi'e'huan 落成
// qie'huan,音节数与字数对不上、do_validate 静默失败而不入库。整串是完整
// 拼音时改用带撇号的 normalized_segmentation 作 canonical 键,按用户实际断句
// 落库。简拼 / 带 helpcode 等非完整拼音的云候选仍走原来的 committed_pinyin
// 路径(见下面的 else),避免回归。
cloudCommittedPinyin = g_inputSession->get_pinyin_segmentation();
cloudCommittedPinyinIsCanonical = true;
}
else
{
cloudCommittedPinyin = g_inputSession->get_cloud_query_state().committed_pinyin;
}
}
if (curWordItem.source == CandidateSource::AiSuggestion)
{
if (g_inputSession->is_all_complete_pure_pinyin())
{
aiCommittedPinyin = curWordItem.canonical_pinyin.empty()
? g_inputSession->get_cloud_query_state().committed_pinyin
: curWordItem.canonical_pinyin;
aiCommittedPinyinIsCanonical = !curWordItem.canonical_pinyin.empty();
if (!curWordItem.canonical_pinyin.empty())
{
aiCommittedPinyin = curWordItem.canonical_pinyin;
aiCommittedPinyinIsCanonical = true;
}
else
{
// AI 候选自己不带 canonical_pinyin。committed_pinyin 走的是
// normalized_input,那串已经去掉了音节边界(见 quanpin_scheme 里
// 只往 normalized_input 里塞非撇号字符),create_word 会用贪心的
// "correction" 切分重新断句:qi'e'huan 落成 qie'huan,音节数与字数
// 对不上,do_validate 直接判失败、静默不入库,于是用户再打同样的音
// 时这条 AI 联想仍然要靠现场猜。这里改用带撇号的 normalized_segmentation
// (即屏幕上的分段,is_all_complete_pure_pinyin 已保证它整串是完整音节)
// 作 canonical 键,create_word_from_canonical_pinyin 按撇号 split、不再
// 重新贪心断句,用户实际选的那条读音就能正确落库。
aiCommittedPinyin = g_inputSession->get_pinyin_segmentation();
aiCommittedPinyinIsCanonical = true;
}
}
isNeedUpdateWeight = false;
}
@@ -4893,7 +4928,7 @@ void ProcessSelectionKey(UINT keycode, uint64_t client_id, uint64_t activation_e
// 看看云联想出来的词是否需要被插入到数据库
if (curWordItem.source == CandidateSource::CloudSuggestion && !cloudCommittedPinyin.empty())
{
EnqueueStoreUserPhraseTask(cloudCommittedPinyin, curWord);
EnqueueStoreUserPhraseTask(cloudCommittedPinyin, curWord, cloudCommittedPinyinIsCanonical);
// 清理云联想变量状态
Global::cloud_candidate.added = false;
Global::cloud_candidate.word.clear();