import Foundation public struct TalkDirective: Equatable, Sendable { public var voiceId: String? public var modelId: String? public var speed: Double? public var rateWPM: Int? public var stability: Double? public var similarity: Double? public var style: Double? public var speakerBoost: Bool? public var seed: Int? public var normalize: String? public var language: String? public var outputFormat: String? public var latencyTier: Int? public var once: Bool? public init( voiceId: String? = nil, modelId: String? = nil, speed: Double? = nil, rateWPM: Int? = nil, stability: Double? = nil, similarity: Double? = nil, style: Double? = nil, speakerBoost: Bool? = nil, seed: Int? = nil, normalize: String? = nil, language: String? = nil, outputFormat: String? = nil, latencyTier: Int? = nil, once: Bool? = nil) { self.voiceId = voiceId self.modelId = modelId self.speed = speed self.rateWPM = rateWPM self.stability = stability self.similarity = similarity self.style = style self.speakerBoost = speakerBoost self.seed = seed self.normalize = normalize self.language = language self.outputFormat = outputFormat self.latencyTier = latencyTier self.once = once } } public struct TalkDirectiveParseResult: Equatable, Sendable { public let directive: TalkDirective? public let stripped: String public let unknownKeys: [String] public init(directive: TalkDirective?, stripped: String, unknownKeys: [String]) { self.directive = directive self.stripped = stripped self.unknownKeys = unknownKeys } } public enum TalkDirectiveParser { public static func parse(_ text: String) -> TalkDirectiveParseResult { let normalized = text.replacingOccurrences(of: "\r\n", with: "\n") var lines = normalized.split(separator: "\n", omittingEmptySubsequences: false) guard !lines.isEmpty else { return TalkDirectiveParseResult(directive: nil, stripped: text, unknownKeys: []) } guard let firstNonEmptyIndex = lines.firstIndex(where: { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }) else { return TalkDirectiveParseResult(directive: nil, stripped: text, unknownKeys: []) } var firstNonEmpty = firstNonEmptyIndex if firstNonEmpty > 0 { lines.removeSubrange(0.. String? { for key in keys { if let value = dict[key] as? String { let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines) if !trimmed.isEmpty { return trimmed } } } return nil } private static func doubleValue(_ dict: [String: Any], keys: [String]) -> Double? { for key in keys { if let value = dict[key] as? Double { return value } if let value = dict[key] as? Int { return Double(value) } if let value = dict[key] as? String, let parsed = Double(value) { return parsed } } return nil } private static func intValue(_ dict: [String: Any], keys: [String]) -> Int? { for key in keys { if let value = dict[key] as? Int { return value } if let value = dict[key] as? Double { return Int(value) } if let value = dict[key] as? String, let parsed = Int(value) { return parsed } } return nil } private static func boolValue(_ dict: [String: Any], keys: [String]) -> Bool? { for key in keys { if let value = dict[key] as? Bool { return value } if let value = dict[key] as? String { let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() if ["true", "yes", "1"].contains(trimmed) { return true } if ["false", "no", "0"].contains(trimmed) { return false } } } return nil } }