Files
msime-windows/engine/core/pinyin_decoder.cpp
T
fanlusky fae80660c1 feat(engine,server,windows,ui-html): 双拼句中辅助码
在完整音节后敲反引号,接着的字母筛这个音节上的字:第一码大小写均可,
紧跟的大写字母是第二码,之后可以接着打,一个输入里可以有多段。开关
helpcode.shuangpin_mid_sentence_helpcode 默认关闭。

约束在整句解码时生效,而不是解完再筛:
- Google 解码器在 add_char_qwerty 里送 extend_mtrx_nd 之前按约束过滤词条,
  锁住的字带着上下文重新整句解码;约束变化时整段重解,不污染增量搜索。
- kenlm 词格建图时按约束筛边,受约束的跨度不截断地查词库;神经重排只在
  词格 n-best 里挑,自动满足约束。
- 普通词条、云和 AI 候选按同一规则收尾校验;满足不了就不出整句。

反引号段在请求里换成手动分隔符,原串留给宿主回读与预编辑显示;选词推进
时剩余音节上的约束随原串保留。输入形状规则放在 contracts/mid_sentence_helpcode.h,
TSF、Server、引擎共用,新增 Worker opcode MidSentenceHelpcodeChanged = 28。
googlepinyinime-rev 已与上游解绑,AGENTS 与 UPSTREAM.md 同步说明。
2026-10-02 02:20:37 +08:00

119 lines
4.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pinyin_decoder.h"
#include "data_path.h"
#include "../googlepinyinime-rev/src/include/pinyinime.h"
#include "../googlepinyinime-rev/src/include/lpicache.h"
#include <mutex>
#include <utf8.h>
namespace metasequoia
{
namespace
{
struct DecoderService
{
std::mutex mutex;
std::filesystem::path model;
std::filesystem::path user_dictionary;
bool ready = false;
std::size_t clients = 0;
~DecoderService()
{
ime_pinyin::im_close_decoder();
}
};
DecoderService &service()
{
static DecoderService instance;
return instance;
}
bool accept_constrained_char(std::size_t index, ime_pinyin::char16 hanzi, void *user)
{
const auto &constraints = *static_cast<const std::vector<DecoderCharConstraint> *>(user);
if (index >= constraints.size() || !constraints[index].accept)
return true;
const std::u16string text(1, static_cast<char16_t>(hanzi));
return constraints[index].accept(utf8::utf16to8(text));
}
} // namespace
PinyinDecoder::PinyinDecoder(std::filesystem::path model, std::filesystem::path user_dictionary)
: model_(std::move(model)), user_dictionary_(std::move(user_dictionary))
{
auto &decoder = service();
std::lock_guard lock(decoder.mutex);
++decoder.clients;
}
PinyinDecoder::~PinyinDecoder()
{
auto &decoder = service();
std::lock_guard lock(decoder.mutex);
if (--decoder.clients == 0)
{
ime_pinyin::im_close_decoder();
decoder.ready = false;
decoder.model.clear();
decoder.user_dictionary.clear();
}
}
std::string PinyinDecoder::sentence(const std::string &pinyin,
const std::vector<DecoderCharConstraint> &constraints) const
{
if (pinyin.empty() || pinyin.size() > 128 || model_.empty() || user_dictionary_.empty())
return {};
auto &decoder = service();
std::lock_guard lock(decoder.mutex);
if (decoder.model != model_ || decoder.user_dictionary != user_dictionary_)
{
ime_pinyin::im_close_decoder();
decoder.ready = false;
// The upstream lemma cache is also global and contains model-specific IDs.
auto &cache = ime_pinyin::LpiCache::get_instance();
for (ime_pinyin::uint16 id = 0; id < ime_pinyin::kFullSplIdStart; ++id)
cache.put_cache(id, nullptr, 0);
decoder.model = model_;
decoder.user_dictionary = user_dictionary_;
decoder.ready =
ime_pinyin::im_open_decoder(path_to_utf8(model_).c_str(), path_to_utf8(user_dictionary_).c_str());
if (!decoder.ready)
return {};
ime_pinyin::im_set_max_lens(128, 64);
}
if (!decoder.ready)
return {};
std::vector<ime_pinyin::uint16> positions;
positions.reserve(constraints.size());
for (const auto &constraint : constraints)
{
if (constraint.pinyin_offset >= pinyin.size())
return {};
positions.push_back(static_cast<ime_pinyin::uint16>(constraint.pinyin_offset));
}
// 约束变化时解码器会整段重解,增量搜索只在前后都不带约束时继续生效。
ime_pinyin::im_set_char_constraints(positions.data(), positions.size(), accept_constrained_char,
const_cast<std::vector<DecoderCharConstraint> *>(&constraints));
// 不先 im_reset_search:upstream 的 search 自己会与上一次的输入比公共前缀,从分歧处
// 回退再往后解,连续打字时每键只多解一个字母。前缀为零时它走的就是整段重置。
const auto count = ime_pinyin::im_search(pinyin.data(), pinyin.size());
std::string result;
for (std::size_t i = 0; i < count && result.empty(); ++i)
{
ime_pinyin::char16 buffer[256] = {};
if (!ime_pinyin::im_get_candidate(i, buffer, 255))
continue;
std::size_t length = 0;
while (length < 255 && buffer[length] != 0)
++length;
if (length)
result = utf8::utf16to8(std::u16string(reinterpret_cast<const char16_t *>(buffer), length));
}
// 回调的 user 指针指向调用方的 constraints,出了这个函数就悬空,不能留在解码器里。
if (!constraints.empty())
ime_pinyin::im_set_char_constraints(nullptr, 0, nullptr, nullptr);
return result;
}
} // namespace metasequoia