#pragma once #include #include #include #include #include "iasr.h" // 可脚本化的假推理后端:让流式策略与实时管线可以脱离真模型测试。 // // dropLastTokens 用"空格分词"模拟词表:丢掉最后 N 个词,并保留原有格式 // (这点很重要 —— 续写开头那个空格是有意义的)。 class FakeAsr : public lc::IAsr { public: // 每次 streamAppend 依次返回这里的文本;用完后返回空串 std::vector scripted; // 记录(供断言用) std::vector prefixesSeen; std::vector turnsSeen; std::vector samplesSeen; int resets = 0; int sampleRate() const override { return 16000; } lc::AsrResult transcribeWithPrefix(const float *, size_t, const std::string &) override { lc::AsrResult r; r.ok = true; if (call_ < scripted.size()) { r.raw = scripted[call_]; r.transcript = lc::parseAsrOutput(r.raw); } ++call_; return r; } lc::AsrStreamStep streamAppend(const float *, size_t nSamples, const std::string & prefix, int) override { prefixesSeen.push_back(prefix); samplesSeen.push_back(nSamples); lc::AsrStreamStep s; s.ok = true; if (call_ < scripted.size()) s.newText = scripted[call_]; ++call_; s.newTokens = 1; s.encodeSeconds = 0.001; s.inferSeconds = 0.001; return s; } lc::AsrStreamStep streamAppendTurn(const float *, size_t nSamples, int) override { samplesSeen.push_back(nSamples); lc::AsrStreamStep s; s.ok = true; if (call_ < scripted.size()) s.newText = scripted[call_]; ++call_; turnsSeen.push_back(s.newText); s.newTokens = 1; s.inferSeconds = 0.001; return s; } void streamReset() override { ++resets; } std::string dropLastTokens(const std::string & text, int dropLast) const override { if (dropLast <= 0 || text.empty()) return text; const auto isSpace = [](char c) { return std::isspace((unsigned char) c) != 0; }; size_t pos = text.size(); for (int i = 0; i < dropLast; ++i) { while (pos > 0 && isSpace(text[pos - 1])) --pos; // 跳过尾部空白 while (pos > 0 && !isSpace(text[pos - 1])) --pos; // 跳过一个词 if (pos == 0) return {}; } while (pos > 0 && isSpace(text[pos - 1])) --pos; return text.substr(0, pos); } private: size_t call_ = 0; };