feat(stream): add append-only streaming mode with flat cost

Add an append-only streaming path that feeds each audio chunk as a new
conversation turn, so nothing is ever recomputed: the KV grows by
append, no prefix is re-decoded and no audio is re-encoded. Per-step
cost becomes flat instead of growing with the segment (RTF 1.06 -> 0.26
on 62.6s of continuous speech), but accuracy requires ~8s chunks because
the audio encoder needs its 8s attention window, so text only appears
every 8s. The incremental mode stays the default.

新增追加式流式:每块音频作为一个新的对话轮追加,前缀不重算、旧音频不重
编码,单步成本不再随段落增长(连续 62.6s 语音 RTF 从 1.06 降到 0.26)。
但准确率要求 chunk≈8s,否则会被截断,所以出字粒度是 8s;增量式仍为默认。
This commit is contained in:
blumia committed 2026-10-12 14:22:29 +08:00
1 parent 7e5257429a
commit 85b206f1cd
10 files changed
+330 -18

No files matched your search

+27
View File
@@ -150,6 +150,33 @@ private slots:
QVERIFY2(all.contains("hello there world"), qPrintable(all));
}
// 追加式:每块音频作为新一轮追加,文本立即定稿,不回喂任何前缀
void appendModeCommitsEachChunkImmediately()
{
FakeAsr fake;
fake.scripted = {"language English<asr_text>one two",
" language English<asr_text> three four"};
lc::StreamOptions o = baseOptions();
o.appendOnly = true;
lc::StreamingAsr stream(fake, o);
std::vector<lc::StreamingAsr::Step> steps;
const auto cb = [&steps](const lc::StreamingAsr::Step & s) { steps.push_back(s); };
const auto c = chunk(16000);
stream.feed(c.data(), c.size(), cb);
stream.feed(c.data(), c.size(), cb);
QCOMPARE(fake.turnsSeen.size(), size_t(2));
QVERIFY2(fake.prefixesSeen.empty(), "追加式不应该回喂前缀");
QCOMPARE(steps.size(), size_t(2));
QCOMPARE(q(steps[0].committed), QString("one two"));
QVERIFY(steps[0].tail.empty()); // 追加式没有"不稳定尾部"
QVERIFY(q(steps[1].committed).contains("three four"));
}
// 同一次 feed 里跨多块也要正确切分
void splitsMultipleChunksInOneFeed()
{