mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 07:07:27 +00:00
refactor(ios): drop legacy talk payload and keychain fallbacks
This commit is contained in:
@@ -26,7 +26,6 @@ enum GatewaySettingsStore {
|
|||||||
private static let preferredGatewayStableIDAccount = "preferredStableID"
|
private static let preferredGatewayStableIDAccount = "preferredStableID"
|
||||||
private static let lastDiscoveredGatewayStableIDAccount = "lastDiscoveredStableID"
|
private static let lastDiscoveredGatewayStableIDAccount = "lastDiscoveredStableID"
|
||||||
private static let talkProviderApiKeyAccountPrefix = "provider.apiKey."
|
private static let talkProviderApiKeyAccountPrefix = "provider.apiKey."
|
||||||
private static let talkElevenLabsApiKeyLegacyAccount = "elevenlabs.apiKey"
|
|
||||||
|
|
||||||
static func bootstrapPersistence() {
|
static func bootstrapPersistence() {
|
||||||
self.ensureStableInstanceID()
|
self.ensureStableInstanceID()
|
||||||
@@ -154,18 +153,6 @@ enum GatewaySettingsStore {
|
|||||||
account: account)?
|
account: account)?
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
if value?.isEmpty == false { return value }
|
if value?.isEmpty == false { return value }
|
||||||
|
|
||||||
if providerId == "elevenlabs" {
|
|
||||||
let legacyValue = KeychainStore.loadString(
|
|
||||||
service: self.talkService,
|
|
||||||
account: self.talkElevenLabsApiKeyLegacyAccount)?
|
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
if legacyValue?.isEmpty == false {
|
|
||||||
_ = KeychainStore.saveString(legacyValue!, service: self.talkService, account: account)
|
|
||||||
return legacyValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,23 +162,9 @@ enum GatewaySettingsStore {
|
|||||||
let trimmed = apiKey?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
let trimmed = apiKey?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
if trimmed.isEmpty {
|
if trimmed.isEmpty {
|
||||||
_ = KeychainStore.delete(service: self.talkService, account: account)
|
_ = KeychainStore.delete(service: self.talkService, account: account)
|
||||||
if providerId == "elevenlabs" {
|
|
||||||
_ = KeychainStore.delete(service: self.talkService, account: self.talkElevenLabsApiKeyLegacyAccount)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = KeychainStore.saveString(trimmed, service: self.talkService, account: account)
|
_ = KeychainStore.saveString(trimmed, service: self.talkService, account: account)
|
||||||
if providerId == "elevenlabs" {
|
|
||||||
_ = KeychainStore.delete(service: self.talkService, account: self.talkElevenLabsApiKeyLegacyAccount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func loadTalkElevenLabsApiKey() -> String? {
|
|
||||||
self.loadTalkProviderApiKey(provider: "elevenlabs")
|
|
||||||
}
|
|
||||||
|
|
||||||
static func saveTalkElevenLabsApiKey(_ apiKey: String?) {
|
|
||||||
self.saveTalkProviderApiKey(apiKey, provider: "elevenlabs")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func saveLastGatewayConnectionManual(host: String, port: Int, useTLS: Bool, stableID: String) {
|
static func saveLastGatewayConnectionManual(host: String, port: Int, useTLS: Bool, stableID: String) {
|
||||||
|
|||||||
@@ -1889,7 +1889,6 @@ extension TalkModeManager {
|
|||||||
struct TalkProviderConfigSelection {
|
struct TalkProviderConfigSelection {
|
||||||
let provider: String
|
let provider: String
|
||||||
let config: [String: Any]
|
let config: [String: Any]
|
||||||
let normalizedPayload: Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func normalizedTalkProviderID(_ raw: String?) -> String? {
|
private static func normalizedTalkProviderID(_ raw: String?) -> String? {
|
||||||
@@ -1901,29 +1900,22 @@ extension TalkModeManager {
|
|||||||
guard let talk else { return nil }
|
guard let talk else { return nil }
|
||||||
let rawProvider = talk["provider"] as? String
|
let rawProvider = talk["provider"] as? String
|
||||||
let rawProviders = talk["providers"] as? [String: Any]
|
let rawProviders = talk["providers"] as? [String: Any]
|
||||||
let hasNormalized = rawProvider != nil || rawProviders != nil
|
guard rawProvider != nil || rawProviders != nil else { return nil }
|
||||||
if hasNormalized {
|
let providers = rawProviders ?? [:]
|
||||||
let providers = rawProviders ?? [:]
|
let normalizedProviders = providers.reduce(into: [String: [String: Any]]()) { acc, entry in
|
||||||
let normalizedProviders = providers.reduce(into: [String: [String: Any]]()) { acc, entry in
|
guard
|
||||||
guard
|
let providerID = Self.normalizedTalkProviderID(entry.key),
|
||||||
let providerID = Self.normalizedTalkProviderID(entry.key),
|
let config = entry.value as? [String: Any]
|
||||||
let config = entry.value as? [String: Any]
|
else { return }
|
||||||
else { return }
|
acc[providerID] = config
|
||||||
acc[providerID] = config
|
|
||||||
}
|
|
||||||
let providerID =
|
|
||||||
Self.normalizedTalkProviderID(rawProvider) ??
|
|
||||||
normalizedProviders.keys.sorted().first ??
|
|
||||||
Self.defaultTalkProvider
|
|
||||||
return TalkProviderConfigSelection(
|
|
||||||
provider: providerID,
|
|
||||||
config: normalizedProviders[providerID] ?? [:],
|
|
||||||
normalizedPayload: true)
|
|
||||||
}
|
}
|
||||||
|
let providerID =
|
||||||
|
Self.normalizedTalkProviderID(rawProvider) ??
|
||||||
|
normalizedProviders.keys.sorted().first ??
|
||||||
|
Self.defaultTalkProvider
|
||||||
return TalkProviderConfigSelection(
|
return TalkProviderConfigSelection(
|
||||||
provider: Self.defaultTalkProvider,
|
provider: providerID,
|
||||||
config: talk,
|
config: normalizedProviders[providerID] ?? [:])
|
||||||
normalizedPayload: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadConfig() async {
|
func reloadConfig() async {
|
||||||
@@ -1934,6 +1926,10 @@ extension TalkModeManager {
|
|||||||
guard let config = json["config"] as? [String: Any] else { return }
|
guard let config = json["config"] as? [String: Any] else { return }
|
||||||
let talk = config["talk"] as? [String: Any]
|
let talk = config["talk"] as? [String: Any]
|
||||||
let selection = Self.selectTalkProviderConfig(talk)
|
let selection = Self.selectTalkProviderConfig(talk)
|
||||||
|
if talk != nil, selection == nil {
|
||||||
|
GatewayDiagnostics.log(
|
||||||
|
"talk config ignored: legacy payload unsupported on iOS beta; expected talk.provider/providers")
|
||||||
|
}
|
||||||
let activeProvider = selection?.provider ?? Self.defaultTalkProvider
|
let activeProvider = selection?.provider ?? Self.defaultTalkProvider
|
||||||
let activeConfig = selection?.config
|
let activeConfig = selection?.config
|
||||||
self.defaultVoiceId = (activeConfig?["voiceId"] as? String)?
|
self.defaultVoiceId = (activeConfig?["voiceId"] as? String)?
|
||||||
@@ -1983,7 +1979,7 @@ extension TalkModeManager {
|
|||||||
if let interrupt = talk?["interruptOnSpeech"] as? Bool {
|
if let interrupt = talk?["interruptOnSpeech"] as? Bool {
|
||||||
self.interruptOnSpeech = interrupt
|
self.interruptOnSpeech = interrupt
|
||||||
}
|
}
|
||||||
if selection?.normalizedPayload == true {
|
if selection != nil {
|
||||||
GatewayDiagnostics.log("talk config provider=\(activeProvider)")
|
GatewayDiagnostics.log("talk config provider=\(activeProvider)")
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ private let talkService = "ai.openclaw.talk"
|
|||||||
private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId")
|
private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId")
|
||||||
private let preferredGatewayEntry = KeychainEntry(service: gatewayService, account: "preferredStableID")
|
private let preferredGatewayEntry = KeychainEntry(service: gatewayService, account: "preferredStableID")
|
||||||
private let lastGatewayEntry = KeychainEntry(service: gatewayService, account: "lastDiscoveredStableID")
|
private let lastGatewayEntry = KeychainEntry(service: gatewayService, account: "lastDiscoveredStableID")
|
||||||
private let talkElevenLabsLegacyEntry = KeychainEntry(service: talkService, account: "elevenlabs.apiKey")
|
|
||||||
private let talkElevenLabsProviderEntry = KeychainEntry(
|
|
||||||
service: talkService,
|
|
||||||
account: "provider.apiKey.elevenlabs")
|
|
||||||
private let talkAcmeProviderEntry = KeychainEntry(service: talkService, account: "provider.apiKey.acme")
|
private let talkAcmeProviderEntry = KeychainEntry(service: talkService, account: "provider.apiKey.acme")
|
||||||
|
|
||||||
private func snapshotDefaults(_ keys: [String]) -> [String: Any?] {
|
private func snapshotDefaults(_ keys: [String]) -> [String: Any?] {
|
||||||
@@ -215,21 +211,4 @@ private func restoreKeychain(_ snapshot: [KeychainEntry: String?]) {
|
|||||||
GatewaySettingsStore.saveTalkProviderApiKey(nil, provider: "acme")
|
GatewaySettingsStore.saveTalkProviderApiKey(nil, provider: "acme")
|
||||||
#expect(GatewaySettingsStore.loadTalkProviderApiKey(provider: "acme") == nil)
|
#expect(GatewaySettingsStore.loadTalkProviderApiKey(provider: "acme") == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func talkProviderApiKey_elevenlabsLegacyFallbackMigratesToProviderKey() {
|
|
||||||
let keychainSnapshot = snapshotKeychain([talkElevenLabsLegacyEntry, talkElevenLabsProviderEntry])
|
|
||||||
defer { restoreKeychain(keychainSnapshot) }
|
|
||||||
|
|
||||||
_ = KeychainStore.delete(service: talkService, account: talkElevenLabsProviderEntry.account)
|
|
||||||
_ = KeychainStore.saveString(
|
|
||||||
"legacy-eleven-key",
|
|
||||||
service: talkService,
|
|
||||||
account: talkElevenLabsLegacyEntry.account)
|
|
||||||
|
|
||||||
let loaded = GatewaySettingsStore.loadTalkProviderApiKey(provider: "elevenlabs")
|
|
||||||
#expect(loaded == "legacy-eleven-key")
|
|
||||||
#expect(
|
|
||||||
KeychainStore.loadString(service: talkService, account: talkElevenLabsProviderEntry.account)
|
|
||||||
== "legacy-eleven-key")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import Testing
|
import Testing
|
||||||
@testable import OpenClaw
|
@testable import OpenClaw
|
||||||
|
|
||||||
|
@MainActor
|
||||||
@Suite struct TalkModeConfigParsingTests {
|
@Suite struct TalkModeConfigParsingTests {
|
||||||
@Test func prefersNormalizedTalkProviderPayload() async {
|
@Test func prefersNormalizedTalkProviderPayload() {
|
||||||
let talk: [String: Any] = [
|
let talk: [String: Any] = [
|
||||||
"provider": "elevenlabs",
|
"provider": "elevenlabs",
|
||||||
"providers": [
|
"providers": [
|
||||||
@@ -13,22 +14,18 @@ import Testing
|
|||||||
"voiceId": "voice-legacy",
|
"voiceId": "voice-legacy",
|
||||||
]
|
]
|
||||||
|
|
||||||
let selection = await MainActor.run { TalkModeManager.selectTalkProviderConfig(talk) }
|
let selection = TalkModeManager.selectTalkProviderConfig(talk)
|
||||||
#expect(selection?.provider == "elevenlabs")
|
#expect(selection?.provider == "elevenlabs")
|
||||||
#expect(selection?.normalizedPayload == true)
|
|
||||||
#expect(selection?.config["voiceId"] as? String == "voice-normalized")
|
#expect(selection?.config["voiceId"] as? String == "voice-normalized")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func fallsBackToLegacyTalkFieldsWhenNormalizedPayloadMissing() async {
|
@Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() {
|
||||||
let talk: [String: Any] = [
|
let talk: [String: Any] = [
|
||||||
"voiceId": "voice-legacy",
|
"voiceId": "voice-legacy",
|
||||||
"apiKey": "legacy-key",
|
"apiKey": "legacy-key",
|
||||||
]
|
]
|
||||||
|
|
||||||
let selection = await MainActor.run { TalkModeManager.selectTalkProviderConfig(talk) }
|
let selection = TalkModeManager.selectTalkProviderConfig(talk)
|
||||||
#expect(selection?.provider == "elevenlabs")
|
#expect(selection == nil)
|
||||||
#expect(selection?.normalizedPayload == false)
|
|
||||||
#expect(selection?.config["voiceId"] as? String == "voice-legacy")
|
|
||||||
#expect(selection?.config["apiKey"] as? String == "legacy-key")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user