feat(ai): unify reply center and refresh keyboard AI features

- Merge invitation, task, blessing, clarification, and empathy actions
  into a single Reply flow, with three fixed, clearly labeled stance
  choices whenever user intent must not be guessed.
- Refine clipboard semantic routing with bilingual schedule,
  confirmation, and follow-up models, conservative language thresholds,
  and explicit-assignment guard for complaint-only text.
- Persist Apple account refresh state, harden session recovery, and
  surface durable account diagnostics across keyboard and app.
- Derive personal-style prompts through two-stage corpus evidence and
  apply real low-confidence ASR tendencies instead of neutral templates.
- Localize the new reply center, clipboard semantics, and personal-style
  surfaces in both English and Simplified Chinese.
This commit is contained in:
Rocky
2026-08-29 11:51:21 +08:00
parent f91a8f2456
commit 3c10d73d7f
30 changed files with 2090 additions and 447 deletions
@@ -12,6 +12,7 @@ public enum LLMError: Error, LocalizedError, Sendable, Equatable {
case http(status: Int)
case decoding(String)
case transport(String)
case timeout
case cancelled
case rateLimited
@@ -27,6 +28,8 @@ public enum LLMError: Error, LocalizedError, Sendable, Equatable {
return SharedL10n.string("error.llm.decoding")
case .transport:
return SharedL10n.string("error.llm.transport")
case .timeout:
return SharedL10n.string("error.llm.timeout")
case .rateLimited:
return SharedL10n.string("error.llm.rateLimited")
case .cancelled:
@@ -291,6 +294,8 @@ public struct OpenAICompatibleClient: LLMClient {
throw LLMError.cancelled
} catch let urlError as URLError where urlError.code == .cancelled {
throw LLMError.cancelled
} catch let urlError as URLError where urlError.code == .timedOut {
throw LLMError.timeout
} catch {
throw LLMError.transport(String(describing: error))
}