Skip to content

Commit d4162cf

Browse files
authored
Merge pull request #412 from Open-Less/pr/streaming-cross-platform
feat: cross-platform streaming polish output (Win/Linux/macOS)
2 parents af07795 + ceda94e commit d4162cf

15 files changed

Lines changed: 1185 additions & 6 deletions

File tree

openless-all/app/src-tauri/src/coordinator.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,6 +2015,105 @@ fn ensure_qa_volcengine_credentials() -> Result<(), String> {
20152015

20162016
/// 润色文本;失败时返回原文 + 失败原因,调用方据此弹错误胶囊 + 写历史 error_code。
20172017
/// 之前固定返回 String,调用方拿不到失败信号 → 用户感知"为什么风格设置没生效"。issue #57。
2018+
/// 流式润色的三态结果。让上层(dictation pipeline)能区分「已经流出去了」、
2019+
/// 「降级到一次性」和「真失败了走 raw 兜底」三种 case。
2020+
pub enum StreamingPolishOutcome {
2021+
/// 流式润色成功,`String` 是已经一边流一边交给 `on_delta` 的全部文本(用于写
2022+
/// history、做词条命中统计)。调用方不应再 `inserter.insert(&text)`,因为字符
2023+
/// 已经通过键盘事件落到光标处。
2024+
Streamed(String),
2025+
/// 当前配置不支持流式:用户没开 streaming_insert / Gemini provider / Codex
2026+
/// provider / Raw 模式 / 翻译模式 / 不是 macOS。调用方应回到现有的
2027+
/// `polish_or_passthrough` 一次性路径,跟历史行为完全一致。
2028+
UnsupportedFallback,
2029+
/// 流式过程中失败(HTTP / 解析 / 空流等)。`String` 是失败原因,调用方应当
2030+
/// 走 raw 兜底(同 `polish_or_passthrough` 失败分支的语义)。
2031+
Failed(String),
2032+
}
2033+
2034+
/// 流式润色入口。在不支持流式的所有 case 都返回 `UnsupportedFallback`,让调用方
2035+
/// 透明降级。不修改任何持久化 / 焦点 / 光标状态。
2036+
///
2037+
/// `on_delta` 每收到一个 SSE chunk 就被调用一次(同步),调用方负责把 chunk 实际
2038+
/// 模拟键盘事件落到光标 —— 见 `coordinator/dictation.rs` 的流式分支。
2039+
/// `should_cancel` 用户取消时返回 true,立即 break SSE 读循环避免烧 quota。
2040+
pub async fn polish_or_passthrough_streaming<F, C>(
2041+
raw: &RawTranscript,
2042+
mode: PolishMode,
2043+
hotwords: &[String],
2044+
working_languages: &[String],
2045+
chinese_script_preference: ChineseScriptPreference,
2046+
output_language_preference: OutputLanguagePreference,
2047+
llm_thinking_enabled: bool,
2048+
front_app: Option<&str>,
2049+
prior_turns: &[(String, String)],
2050+
on_delta: F,
2051+
should_cancel: C,
2052+
) -> StreamingPolishOutcome
2053+
where
2054+
F: Fn(&str) + Send + Sync,
2055+
C: Fn() -> bool + Send + Sync,
2056+
{
2057+
if mode == PolishMode::Raw {
2058+
log::info!("[coord] streaming polish skipped: mode=Raw, fall back to one-shot");
2059+
return StreamingPolishOutcome::UnsupportedFallback;
2060+
}
2061+
let active_llm = CredentialsVault::get_active_llm();
2062+
if active_llm == "gemini" {
2063+
log::info!(
2064+
"[coord] streaming polish skipped: active LLM provider=gemini (v1 not implemented), fall back to one-shot"
2065+
);
2066+
return StreamingPolishOutcome::UnsupportedFallback;
2067+
}
2068+
let provider = match build_active_llm_provider(llm_thinking_enabled) {
2069+
Ok(p) => p,
2070+
Err(e) => {
2071+
log::error!("[coord] streaming polish: build provider failed: {e}");
2072+
return StreamingPolishOutcome::Failed(e.to_string());
2073+
}
2074+
};
2075+
if !provider.supports_streaming_polish() {
2076+
log::info!(
2077+
"[coord] streaming polish skipped: provider does not support streaming (likely codex OAuth), fall back to one-shot"
2078+
);
2079+
return StreamingPolishOutcome::UnsupportedFallback;
2080+
}
2081+
log::info!(
2082+
"[coord] streaming polish START: provider=openai-compatible mode={:?} raw_chars={} prior_turns={}",
2083+
mode,
2084+
raw.text.chars().count(),
2085+
prior_turns.len()
2086+
);
2087+
match provider
2088+
.polish_streaming(
2089+
&raw.text,
2090+
mode,
2091+
hotwords,
2092+
working_languages,
2093+
chinese_script_preference,
2094+
output_language_preference,
2095+
front_app,
2096+
prior_turns,
2097+
on_delta,
2098+
should_cancel,
2099+
)
2100+
.await
2101+
{
2102+
Ok(text) => {
2103+
log::info!(
2104+
"[coord] streaming polish OK: final_chars={}",
2105+
text.chars().count()
2106+
);
2107+
StreamingPolishOutcome::Streamed(text)
2108+
}
2109+
Err(e) => {
2110+
let reason = e.to_string();
2111+
log::error!("[coord] streaming polish FAILED: {reason}");
2112+
StreamingPolishOutcome::Failed(reason)
2113+
}
2114+
}
2115+
}
2116+
20182117
async fn polish_or_passthrough(
20192118
raw: &RawTranscript,
20202119
mode: PolishMode,

0 commit comments

Comments
 (0)