feat(keyboard): ship AI mode surface with streaming search answers

Add the AI keyboard tab, Agent settings, and user-owned LLM key path for 1.7.0, including streaming answers and web-search transports without the built-in DeepSeek fallback.
This commit is contained in:
Rocky
2026-08-11 01:06:27 +08:00
parent f6212dcd2c
commit 3de665d254
81 changed files with 4467 additions and 398 deletions
+23
View File
@@ -8,6 +8,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **AI answer streaming**: AI mode streams visible answer text into the keyboard as the model writes (all AI-mode transports), with throttled App Group updates, search-fallback draft restart, and a “thinking” status before the first token; dictation polish stays non-streaming. / **AI 回答流式输出**:AI 模式在模型开始写正文后将可见答案增量推送到键盘(覆盖全部 AI 传输路径),经 App Group 节流更新;搜索失败回退会清空半截草稿;首 token 前显示「思考中」;听写润色仍为整段返回。
- **AI Agent settings**: Settings home adds an AI Agent row under General, with a Response length preference (Short / Medium / Detailed, default Medium). AI mode injects soft length guidance into the system prompt and syncs the choice via iCloud settings. / **AI Agent 设置**:设置首页在「通用」下方新增 AI Agent 入口,支持「回复篇幅」(简短 / 中等 / 详细,默认中等)。AI 模式将篇幅作为软约束写入 system prompt,并纳入 iCloud 设置同步。
### Changed
- **AI empty-state tip**: center “Tap the microphone to ask AI” in the answer area (horizontal + vertical). / **AI 空状态指引**:「点击麦克风向 AI 提问」在答案区域水平与垂直居中。
### Fixed
- **AI waiting spinner duplicate**: remove the mini ProgressView beside the AI status caption; the mic button spinner remains the sole loading indicator while recognizing or generating. / **AI 等待转圈重复**:去掉 AI 状态文案旁的迷你 ProgressView;识别/生成中仅保留麦克风按钮上的 loading。
- **AI stream UTF-8 mojibake**: SSE framing now accumulates raw bytes and decodes each line as UTF-8, so Chinese AI answers (e.g. weather) no longer appear as Latin-1 garbage like `今天…`. / **AI 流式 UTF-8 乱码**:SSE 行缓冲改为累积原始字节并以 UTF-8 解码,中文 AI 回答(如天气)不再显示为 `今天…` 一类 Latin-1 乱码。
- **Local ASR dictionary correction**: apply deterministic personal-dictionary alias correction before iOS Flow branches into dictation polish or AI question handling, so AI mode keeps local transcript optimization while still skipping cloud LLM polish. / **本地 ASR 词库纠错**:在 iOS Flow 分流到听写润色或 AI 问答前统一应用个人词库别名确定性纠错,使 AI 模式保留本地转写优化,同时继续跳过云端 LLM 润色。
## [1.7.0] - 2026-08-10
### Added
- **AI keyboard mode**: add a temporary multi-turn AI surface that sends raw ASR questions to the configured LLM, keeps the latest scrollable answer for review, and uses a two-step white Insert then green Send action in messaging fields; only inserted answers enter history and AI character statistics. / **AI 键盘模式**:新增临时多轮 AI 输入面,将原始 ASR 问题直接交给已配置模型,滚动展示最新答案,并在即时通信输入框中采用先点白色「插入」、再点绿色「发送」的两步操作;只有真正插入的答案会进入历史与 AI 字数统计。
- **AI mode web search**: AI questions use a dedicated transport that enables provider-side search when available (DeepSeek/OpenAI/xAI Responses `web_search`, Qwen `enable_search`, Zhipu/Anthropic/Moonshot tools), forces thinking on, and silently retries without search on failure — polish stays on plain Chat Completions. / **AI 模式联网搜索**:AI 问答走独立传输层,在服务商支持时启用服务端搜索(DeepSeek/OpenAI/xAI Responses `web_search`、通义 `enable_search`、智谱/Anthropic/Moonshot tools),强制开启 thinking,失败则静默无搜索重试;润色仍走普通 Chat Completions。
- **API key setup guidance**: Home shows a tip when the polish LLM key is missing; the keyboard mic line warns above the microphone. Without a key, dictation still inserts raw ASR text. / **API Key 引导**:未填写润色 API Key 时首页显示提示,键盘麦克风上方同步提醒;无 Key 时听写仍插入原始识别结果。
### Changed
- **User-owned polish keys only**: remove the built-in DeepSeek `PreconfiguredKeys` fallback. Local and cloud polish (and AI mode) require a user-filled API key; AI question timeout is 60s. / **仅使用用户 API Key**:移除内置 DeepSeek `PreconfiguredKeys` 回退。本地/云端润色与 AI 模式均需用户自行填写 API Key;AI 问答超时为 60 秒。
- **OpenAI default model**: preset default is now `gpt-5.4-mini` (Responses `web_search` capable). Existing saved model names are unchanged. / **OpenAI 默认模型**:预设改为 `gpt-5.4-mini`(支持 Responses 联网搜索);用户已保存的模型名不受影响。
- **LLM preset defaults refreshed**: update defaults for Qwen (`qwen-plus-latest`), Zhipu (`glm-4.7-flash`), Moonshot (`kimi-k2.5`), xAI (`grok-4-fast-reasoning`), Gemini (`gemini-3.1-flash-lite`), MiniMax (`MiniMax-M2.7`), SiliconFlow / OpenRouter / CometAPI / CodingPlanX; Anthropic stays on `claude-sonnet-4-6`. Polish and AI mode both resolve the Settings provider/model via the same endpoint helper. / **LLM 预设默认值更新**:通义 / 智谱 / Moonshot / xAI / Gemini / MiniMax 及部分聚合预设已换新默认模型;Anthropic 仍为 `claude-sonnet-4-6`。润色与 AI 模式共用同一套设置中的服务商与模型解析。
- **Privacy policy**: document user-owned LLM keys, AI-mode optional provider web search, and remove built-in DeepSeek wording (in-app + GitHub Pages). / **隐私政策**:说明用户自备 LLM Key、AI 模式可选服务商联网搜索,并移除内置 DeepSeek 表述(App 内 + GitHub Pages)。
- **iPad system globe key**: add the system 🌐 key on both iPad voice and typing surfaces — tap to switch to the next keyboard, long-press to open the system input-mode picker. The key uses UIKit's standard all-touch-events input-mode action and the same SwiftUI chrome as adjacent action keys; iPhone relies on its system-provided switch below the keyboard. / **iPad 系统地球键**:在 iPad 语音面与打字面均新增系统 🌐 键——轻点切换下一个键盘,长按打开系统键盘列表。按键采用 UIKit 标准全触摸事件输入模式动作,并与相邻功能键共用同一套 SwiftUI 键面;iPhone 则使用键盘下方由系统提供的切换入口。
- **iPad typing layout**: the typing keyboard now adapts to iPad — taller 54 pt key rows, a second-row inset that widens in landscape (40 pt) vs portrait (30 pt), a small grey number overlay (10) on the top letter row mirroring iOS, and a content-driven height (≈300 pt on iPad vs 281 pt on iPhone) so the bottom row is never clipped. Metrics live in `TypingLayoutMetrics` and are selected from one controller-owned device-idiom + horizontal-size-class decision. / **iPad 打字布局**:打字键盘现已适配 iPad——键行加高至 54pt,第二行缩进在横屏(40pt)比竖屏(30pt)更宽,首字母行叠加 1–0 小号灰数字(对齐 iOS),并改为内容驱动高度(iPad 约 300ptiPhone 281pt),底部行不再被裁切。尺寸由控制器统一结合设备类型与水平尺寸类判定。
- **Editing toolbar (iPad)**: the typing top bar gains an iOS-style undo / redo / copy / cut cluster on iPad. Undo/redo track the last voice insertion (redo re-applies it); copy/cut read the host selection. Availability is refreshed from `textDidChange` / `selectionDidChange`. / **编辑工具栏(iPad**:打字面顶栏在 iPad 上新增类 iOS 的撤销/重做/拷贝/剪切簇。撤销/重做跟踪上次听写插入(重做可复原),拷贝/剪切读取宿主选区;可用性随 `textDidChange` / `selectionDidChange` 刷新。