diff --git a/apps/ios/Sources/Gateway/GatewaySettingsStore.swift b/apps/ios/Sources/Gateway/GatewaySettingsStore.swift index 264aa8aa50d..49db9bb1bfc 100644 --- a/apps/ios/Sources/Gateway/GatewaySettingsStore.swift +++ b/apps/ios/Sources/Gateway/GatewaySettingsStore.swift @@ -26,7 +26,6 @@ enum GatewaySettingsStore { private static let preferredGatewayStableIDAccount = "preferredStableID" private static let lastDiscoveredGatewayStableIDAccount = "lastDiscoveredStableID" private static let talkProviderApiKeyAccountPrefix = "provider.apiKey." - private static let talkElevenLabsApiKeyLegacyAccount = "elevenlabs.apiKey" static func bootstrapPersistence() { self.ensureStableInstanceID() @@ -154,18 +153,6 @@ enum GatewaySettingsStore { account: account)? .trimmingCharacters(in: .whitespacesAndNewlines) 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 } @@ -175,23 +162,9 @@ enum GatewaySettingsStore { let trimmed = apiKey?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" if trimmed.isEmpty { _ = KeychainStore.delete(service: self.talkService, account: account) - if providerId == "elevenlabs" { - _ = KeychainStore.delete(service: self.talkService, account: self.talkElevenLabsApiKeyLegacyAccount) - } return } _ = 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) { diff --git a/apps/ios/Sources/Voice/TalkModeManager.swift b/apps/ios/Sources/Voice/TalkModeManager.swift index 4e1a67945f1..d0ae9bc5cb2 100644 --- a/apps/ios/Sources/Voice/TalkModeManager.swift +++ b/apps/ios/Sources/Voice/TalkModeManager.swift @@ -1889,7 +1889,6 @@ extension TalkModeManager { struct TalkProviderConfigSelection { let provider: String let config: [String: Any] - let normalizedPayload: Bool } private static func normalizedTalkProviderID(_ raw: String?) -> String? { @@ -1901,29 +1900,22 @@ extension TalkModeManager { guard let talk else { return nil } let rawProvider = talk["provider"] as? String let rawProviders = talk["providers"] as? [String: Any] - let hasNormalized = rawProvider != nil || rawProviders != nil - if hasNormalized { - let providers = rawProviders ?? [:] - let normalizedProviders = providers.reduce(into: [String: [String: Any]]()) { acc, entry in - guard - let providerID = Self.normalizedTalkProviderID(entry.key), - let config = entry.value as? [String: Any] - else { return } - acc[providerID] = config - } - let providerID = - Self.normalizedTalkProviderID(rawProvider) ?? - normalizedProviders.keys.sorted().first ?? - Self.defaultTalkProvider - return TalkProviderConfigSelection( - provider: providerID, - config: normalizedProviders[providerID] ?? [:], - normalizedPayload: true) + guard rawProvider != nil || rawProviders != nil else { return nil } + let providers = rawProviders ?? [:] + let normalizedProviders = providers.reduce(into: [String: [String: Any]]()) { acc, entry in + guard + let providerID = Self.normalizedTalkProviderID(entry.key), + let config = entry.value as? [String: Any] + else { return } + acc[providerID] = config } + let providerID = + Self.normalizedTalkProviderID(rawProvider) ?? + normalizedProviders.keys.sorted().first ?? + Self.defaultTalkProvider return TalkProviderConfigSelection( - provider: Self.defaultTalkProvider, - config: talk, - normalizedPayload: false) + provider: providerID, + config: normalizedProviders[providerID] ?? [:]) } func reloadConfig() async { @@ -1934,6 +1926,10 @@ extension TalkModeManager { guard let config = json["config"] as? [String: Any] else { return } let talk = config["talk"] as? [String: Any] 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 activeConfig = selection?.config self.defaultVoiceId = (activeConfig?["voiceId"] as? String)? @@ -1983,7 +1979,7 @@ extension TalkModeManager { if let interrupt = talk?["interruptOnSpeech"] as? Bool { self.interruptOnSpeech = interrupt } - if selection?.normalizedPayload == true { + if selection != nil { GatewayDiagnostics.log("talk config provider=\(activeProvider)") } } catch { diff --git a/apps/ios/Tests/GatewaySettingsStoreTests.swift b/apps/ios/Tests/GatewaySettingsStoreTests.swift index ec879b3a0f3..0bac4015236 100644 --- a/apps/ios/Tests/GatewaySettingsStoreTests.swift +++ b/apps/ios/Tests/GatewaySettingsStoreTests.swift @@ -13,10 +13,6 @@ private let talkService = "ai.openclaw.talk" private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId") private let preferredGatewayEntry = KeychainEntry(service: gatewayService, account: "preferredStableID") 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 func snapshotDefaults(_ keys: [String]) -> [String: Any?] { @@ -215,21 +211,4 @@ private func restoreKeychain(_ snapshot: [KeychainEntry: String?]) { GatewaySettingsStore.saveTalkProviderApiKey(nil, provider: "acme") #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") - } } diff --git a/apps/ios/Tests/TalkModeConfigParsingTests.swift b/apps/ios/Tests/TalkModeConfigParsingTests.swift index fd5c3d0f392..fd6b535f8a3 100644 --- a/apps/ios/Tests/TalkModeConfigParsingTests.swift +++ b/apps/ios/Tests/TalkModeConfigParsingTests.swift @@ -1,8 +1,9 @@ import Testing @testable import OpenClaw +@MainActor @Suite struct TalkModeConfigParsingTests { - @Test func prefersNormalizedTalkProviderPayload() async { + @Test func prefersNormalizedTalkProviderPayload() { let talk: [String: Any] = [ "provider": "elevenlabs", "providers": [ @@ -13,22 +14,18 @@ import Testing "voiceId": "voice-legacy", ] - let selection = await MainActor.run { TalkModeManager.selectTalkProviderConfig(talk) } + let selection = TalkModeManager.selectTalkProviderConfig(talk) #expect(selection?.provider == "elevenlabs") - #expect(selection?.normalizedPayload == true) #expect(selection?.config["voiceId"] as? String == "voice-normalized") } - @Test func fallsBackToLegacyTalkFieldsWhenNormalizedPayloadMissing() async { + @Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() { let talk: [String: Any] = [ "voiceId": "voice-legacy", "apiKey": "legacy-key", ] - let selection = await MainActor.run { TalkModeManager.selectTalkProviderConfig(talk) } - #expect(selection?.provider == "elevenlabs") - #expect(selection?.normalizedPayload == false) - #expect(selection?.config["voiceId"] as? String == "voice-legacy") - #expect(selection?.config["apiKey"] as? String == "legacy-key") + let selection = TalkModeManager.selectTalkProviderConfig(talk) + #expect(selection == nil) } }