|
|
@ -25,6 +25,41 @@ extension Notification.Name { |
|
|
class AgentServiceImpl: NSObject { |
|
|
class AgentServiceImpl: NSObject { |
|
|
private let TAG = "AgentServiceImpl" |
|
|
private let TAG = "AgentServiceImpl" |
|
|
|
|
|
|
|
|
|
|
|
/// 日志回调:(level, message) -> Void,由 Plugin 层设置,用于将原生日志回传 Dart |
|
|
|
|
|
var logCallback: ((String, String) -> Void)? |
|
|
|
|
|
|
|
|
|
|
|
private let osLog = OSLog(subsystem: "com.agent.service", category: "Agent") |
|
|
|
|
|
|
|
|
|
|
|
private let logChunkSize = 800 |
|
|
|
|
|
|
|
|
|
|
|
internal func _log(_ level: String, _ message: String) { |
|
|
|
|
|
if message.count <= logChunkSize { |
|
|
|
|
|
_logSingle(level, message) |
|
|
|
|
|
} else { |
|
|
|
|
|
let totalParts = (message.count + logChunkSize - 1) / logChunkSize |
|
|
|
|
|
for i in 0..<totalParts { |
|
|
|
|
|
let start = message.index(message.startIndex, offsetBy: i * logChunkSize) |
|
|
|
|
|
let end = message.index(start, offsetBy: min(logChunkSize, message.distance(from: start, to: message.endIndex))) |
|
|
|
|
|
let chunk = String(message[start..<end]) |
|
|
|
|
|
_logSingle(level, "[\(i + 1)/\(totalParts)] \(chunk)") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private func _logSingle(_ level: String, _ message: String) { |
|
|
|
|
|
switch level { |
|
|
|
|
|
case "ERROR": |
|
|
|
|
|
os_log("%{public}@", log: osLog, type: .error, message) |
|
|
|
|
|
case "WARNING": |
|
|
|
|
|
os_log("%{public}@", log: osLog, type: .default, message) |
|
|
|
|
|
case "DEBUG": |
|
|
|
|
|
os_log("%{public}@", log: osLog, type: .debug, message) |
|
|
|
|
|
default: |
|
|
|
|
|
os_log("%{public}@", log: osLog, type: .info, message) |
|
|
|
|
|
} |
|
|
|
|
|
logCallback?(level, message) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static let shared = AgentServiceImpl() |
|
|
static let shared = AgentServiceImpl() |
|
|
|
|
|
|
|
|
// UserDefaults 相关常量 |
|
|
// UserDefaults 相关常量 |
|
|
@ -208,7 +243,7 @@ class AgentServiceImpl: NSObject { |
|
|
self.vipexptime = vipexptime |
|
|
self.vipexptime = vipexptime |
|
|
} |
|
|
} |
|
|
//打印日志输出vipexptime |
|
|
//打印日志输出vipexptime |
|
|
os_log("Vip 过期时间 %d", log: logger, type: .info, vipexptime) |
|
|
_log("INFO", "[Agent] vipexptime=\(vipexptime)") |
|
|
|
|
|
|
|
|
if let insufficientIntegralText = config["insufficientIntegralText"] as? String { |
|
|
if let insufficientIntegralText = config["insufficientIntegralText"] as? String { |
|
|
self.insufficientIntegralText = insufficientIntegralText |
|
|
self.insufficientIntegralText = insufficientIntegralText |
|
|
@ -361,17 +396,15 @@ class AgentServiceImpl: NSObject { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private func initializeChatApiService() -> Bool { |
|
|
private func initializeChatApiService() -> Bool { |
|
|
os_log("开始初始化ChatAPI服务", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] ChatAPI service init start") |
|
|
|
|
|
|
|
|
guard let chatApiService = chatApiService else { |
|
|
guard let chatApiService = chatApiService else { |
|
|
os_log("ChatAPI服务未创建", log: logger, type: .error) |
|
|
_log("ERROR", "[Agent] ChatAPI service not created") |
|
|
sendError("ChatAPI服务未创建", code: "CHATAPI_INIT_ERROR") |
|
|
sendError("ChatAPI服务未创建", code: "CHATAPI_INIT_ERROR") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
os_log("ChatAPI初始化参数: apiKey=%{public}@, baseUrl=%{public}@, model=%{public}@, mcpServer=%{public}@", |
|
|
_log("INFO", "[Agent] ChatAPI init params: apiKey=\(apiKey.isEmpty ? "empty" : "set"), baseUrl=\(baseUrl), model=\(model)") |
|
|
log: logger, type: .info, |
|
|
|
|
|
apiKey.isEmpty ? "空" : "已设置", baseUrl, model, mcpServer) |
|
|
|
|
|
|
|
|
|
|
|
let success = chatApiService.initialize( |
|
|
let success = chatApiService.initialize( |
|
|
apiKey: apiKey, |
|
|
apiKey: apiKey, |
|
|
@ -382,7 +415,7 @@ class AgentServiceImpl: NSObject { |
|
|
musicServiceType:musicServiceType |
|
|
musicServiceType:musicServiceType |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
os_log("ChatAPI服务初始化结果: %{public}@", log: logger, type: .info, success ? "成功" : "失败") |
|
|
_log("INFO", "[Agent] ChatAPI service init result: \(success ? "success" : "failed")") |
|
|
|
|
|
|
|
|
if !success { |
|
|
if !success { |
|
|
sendError("初始化ChatAPI服务失败", code: "CHATAPI_INIT_ERROR") |
|
|
sendError("初始化ChatAPI服务失败", code: "CHATAPI_INIT_ERROR") |
|
|
@ -392,7 +425,7 @@ class AgentServiceImpl: NSObject { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private func checkAdnSetAudioSession() { |
|
|
private func checkAdnSetAudioSession() { |
|
|
os_log("liwei------ 检查并设置音频会话", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] checkAndSetAudioSession") |
|
|
let audioSourceType: AzureAsrHelper.AudioSourceType = BleService.shared.isConnected() ? .external : .microphone |
|
|
let audioSourceType: AzureAsrHelper.AudioSourceType = BleService.shared.isConnected() ? .external : .microphone |
|
|
azureAsrHelper?.performAudioStart(mod: currentRecognitionMode, audioSourceType: audioSourceType, audioDataCallback: nil) |
|
|
azureAsrHelper?.performAudioStart(mod: currentRecognitionMode, audioSourceType: audioSourceType, audioDataCallback: nil) |
|
|
} |
|
|
} |
|
|
@ -405,7 +438,7 @@ class AgentServiceImpl: NSObject { |
|
|
switch mode { |
|
|
switch mode { |
|
|
case "phone_call": |
|
|
case "phone_call": |
|
|
// 通话模式:禁用空闲检测,保持持续激活 |
|
|
// 通话模式:禁用空闲检测,保持持续激活 |
|
|
os_log("通话模式:禁用空闲检测", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] phone_call mode: idle check disabled") |
|
|
return |
|
|
return |
|
|
case "ble_wakeup": |
|
|
case "ble_wakeup": |
|
|
if !isSpeaking { |
|
|
if !isSpeaking { |
|
|
@ -430,7 +463,7 @@ class AgentServiceImpl: NSObject { |
|
|
azureAsrHelper?.disableBluetoothAudio() |
|
|
azureAsrHelper?.disableBluetoothAudio() |
|
|
} |
|
|
} |
|
|
// 未知模式:使用默认的空闲检测 |
|
|
// 未知模式:使用默认的空闲检测 |
|
|
os_log("未知识别模式: %{public}@,使用默认空闲检测", log: logger, type: .error, mode) |
|
|
_log("ERROR", "[Agent] unknown recognition mode: \(mode), using default idle check") |
|
|
startIdleCheck(idleSeconds: maxIdleSeconds) |
|
|
startIdleCheck(idleSeconds: maxIdleSeconds) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -442,32 +475,31 @@ class AgentServiceImpl: NSObject { |
|
|
self.idleTimer = nil |
|
|
self.idleTimer = nil |
|
|
} |
|
|
} |
|
|
guard self.isRecognizing else { return } |
|
|
guard self.isRecognizing else { return } |
|
|
os_log("liwei--------启动空闲检测,超时时间: %.0f秒,模式: %{public}@", |
|
|
self._log("INFO", "[Agent] idle check started, timeout: \(Int(idleSeconds))s, mode: \(self.currentRecognitionMode)") |
|
|
log: self.logger, |
|
|
|
|
|
type: .info, |
|
|
|
|
|
idleSeconds, |
|
|
|
|
|
self.currentRecognitionMode) |
|
|
|
|
|
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .utility)) |
|
|
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .utility)) |
|
|
timer.schedule(deadline: .now() + idleSeconds) |
|
|
timer.schedule(deadline: .now() + idleSeconds) |
|
|
timer.setEventHandler { [weak self] in |
|
|
timer.setEventHandler { [weak self] in |
|
|
guard let self = self else { return } |
|
|
guard let self = self else { return } |
|
|
DispatchQueue.main.async { [weak self] in |
|
|
DispatchQueue.main.async { [weak self] in |
|
|
guard let self = self else { return } |
|
|
guard let self = self else { return } |
|
|
os_log("liwei--------空闲检测触发自动停止,模式: %{public}@", |
|
|
self._log("INFO", "[Agent] idle check triggered, mode: \(self.currentRecognitionMode), speaking: \(self.isSpeaking), aiStreaming: \(self.isAiStreaming)") |
|
|
log: self.logger, |
|
|
if self.isRecognizing { |
|
|
type: .info, |
|
|
if !self.isSpeaking && !self.isAiStreaming { |
|
|
self.currentRecognitionMode) |
|
|
// 条件满足:TTS未播放且AI未流式输出,执行自动停止 |
|
|
//打电话模式下面 不能关闭识别器 |
|
|
BleService.shared.closeCodec() |
|
|
if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming{ |
|
|
if (self.currentRecognitionMode != "phone_call"){ |
|
|
BleService.shared.closeCodec() |
|
|
self.stopRecognition() |
|
|
if (currentRecognitionMode != "phone_call"){ |
|
|
} |
|
|
self.stopRecognition() |
|
|
self.sendEvent(name: "auto_stop", data: [ |
|
|
|
|
|
"reason": "idle_timeout", |
|
|
|
|
|
"seconds": idleSeconds, |
|
|
|
|
|
"mode": self.currentRecognitionMode |
|
|
|
|
|
]) |
|
|
|
|
|
} else { |
|
|
|
|
|
// 条件不满足:TTS或AI仍在工作中,重新调度计时器等待下一轮检查 |
|
|
|
|
|
self._log("INFO", "[Agent] idle check conditions not met (speaking=\(self.isSpeaking), ai=\(self.isAiStreaming)), rescheduling") |
|
|
|
|
|
self.startIdleCheck(idleSeconds: idleSeconds) |
|
|
} |
|
|
} |
|
|
self.sendEvent(name: "auto_stop", data: [ |
|
|
|
|
|
"reason": "idle_timeout", |
|
|
|
|
|
"seconds": idleSeconds, |
|
|
|
|
|
"mode": self.currentRecognitionMode |
|
|
|
|
|
]) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -496,13 +528,13 @@ class AgentServiceImpl: NSObject { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if useBle, mode == "ble_wakeup", currentRecognitionMode == "ble_wakeup", (isRecognizing || isStartingRecognition) { |
|
|
if useBle, mode == "ble_wakeup", currentRecognitionMode == "ble_wakeup", (isRecognizing || isStartingRecognition) { |
|
|
os_log("liwei-------- ble_wakeup 连续触发,识别中/启动中,忽略重复 startRecognition", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] ble_wakeup duplicate trigger ignored, recognizing/starting") |
|
|
audioPlayer?.playStartSound() |
|
|
audioPlayer?.playStartSound() |
|
|
return true |
|
|
return true |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 接收到唤醒信号,打开编码器 (设备侧) |
|
|
// 接收到唤醒信号,打开编码器 (设备侧) |
|
|
print("ai启动语音\(useBle))") |
|
|
_log("INFO", "[Agent] startRecognition useBle=\(useBle)") |
|
|
var openResult = false // 添加openResult变量定义 |
|
|
var openResult = false // 添加openResult变量定义 |
|
|
if useBle{ |
|
|
if useBle{ |
|
|
isKeepResult = false |
|
|
isKeepResult = false |
|
|
@ -526,7 +558,7 @@ class AgentServiceImpl: NSObject { |
|
|
stopTts() |
|
|
stopTts() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
os_log("🎙️ 尝试打开编码器结果: %@", log: logger, type: .info, openResult ? "成功" : "失败") |
|
|
_log("INFO", "[Agent] openEncoder result: \(openResult ? "success" : "failed")") |
|
|
wasMusicPlayingBeforeRecognition = MusicService.shared.isPlaying |
|
|
wasMusicPlayingBeforeRecognition = MusicService.shared.isPlaying |
|
|
|
|
|
|
|
|
// 如果音乐正在播放,则暂停 |
|
|
// 如果音乐正在播放,则暂停 |
|
|
@ -542,7 +574,7 @@ class AgentServiceImpl: NSObject { |
|
|
// 新增:标记为"启动中",用于允许 stop 在启动未完成时也能生效 |
|
|
// 新增:标记为"启动中",用于允许 stop 在启动未完成时也能生效 |
|
|
isStartingRecognition = true |
|
|
isStartingRecognition = true |
|
|
|
|
|
|
|
|
print("ai启动语音=\(audioSourceType)=\(useBle)") |
|
|
_log("INFO", "[Agent] startRecognition audioSource=\(audioSourceType), useBle=\(useBle)") |
|
|
guard let success = azureAsrHelper?.startContinuousRecognition( |
|
|
guard let success = azureAsrHelper?.startContinuousRecognition( |
|
|
mod:self.currentRecognitionMode, |
|
|
mod:self.currentRecognitionMode, |
|
|
audioSourceType: audioSourceType, |
|
|
audioSourceType: audioSourceType, |
|
|
@ -551,20 +583,20 @@ class AgentServiceImpl: NSObject { |
|
|
// 新增:启动失败时复位"启动中"状态 |
|
|
// 新增:启动失败时复位"启动中"状态 |
|
|
isStartingRecognition = false |
|
|
isStartingRecognition = false |
|
|
sendError("启动语音识别失败", code: "RECOGNITION_START_ERROR") |
|
|
sendError("启动语音识别失败", code: "RECOGNITION_START_ERROR") |
|
|
os_log("liwei--------------- 启动语音识别失败: %{public}@", log: logger, type: .info, mode) |
|
|
_log("ERROR", "[Agent] startRecognition failed, mode: \(mode)") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
if(useBle){ |
|
|
if(useBle){ |
|
|
isKeepResult = true |
|
|
isKeepResult = true |
|
|
} |
|
|
} |
|
|
os_log("liwei--------------- 启动语音识别: %{public}@", log: logger, type: .info, mode) |
|
|
_log("INFO", "[Agent] startRecognition success, mode: \(mode)") |
|
|
// 根据模式启动相应的空闲检测(此处 guard isRecognizing,会在 onSessionStarted 中再次启动) |
|
|
// 根据模式启动相应的空闲检测(此处 guard isRecognizing,会在 onSessionStarted 中再次启动) |
|
|
startIdleCheckForMode(mode: mode) |
|
|
startIdleCheckForMode(mode: mode) |
|
|
|
|
|
|
|
|
return true |
|
|
return true |
|
|
} |
|
|
} |
|
|
func recognizeCallback() -> Bool { |
|
|
func recognizeCallback() -> Bool { |
|
|
print("重新设置识别回调=") |
|
|
_log("INFO", "[Agent] recognizeCallback re-setup") |
|
|
guard let asrsetupSuccess = azureAsrHelper?.setupEventListeners( |
|
|
guard let asrsetupSuccess = azureAsrHelper?.setupEventListeners( |
|
|
callback: self |
|
|
callback: self |
|
|
), asrsetupSuccess else { |
|
|
), asrsetupSuccess else { |
|
|
@ -579,7 +611,7 @@ class AgentServiceImpl: NSObject { |
|
|
/// 特性: |
|
|
/// 特性: |
|
|
/// 1) 即便处于"启动中"(isStartingRecognition)也会打印日志并尝试停止 |
|
|
/// 1) 即便处于"启动中"(isStartingRecognition)也会打印日志并尝试停止 |
|
|
/// 2) 如果底层尚未完全进入可停止状态,记录一次待停止请求,待会话启动后立即停止 |
|
|
/// 2) 如果底层尚未完全进入可停止状态,记录一次待停止请求,待会话启动后立即停止 |
|
|
print("停止识别语音,isRecognizing=\(isRecognizing),isStartingRecognition=\(isStartingRecognition)") |
|
|
_log("INFO", "[Agent] stopRecognition, isRecognizing=\(isRecognizing), isStartingRecognition=\(isStartingRecognition)") |
|
|
if !(isRecognizing || isStartingRecognition) { |
|
|
if !(isRecognizing || isStartingRecognition) { |
|
|
// 既不在识别中,也不在启动中,视为幂等处理 |
|
|
// 既不在识别中,也不在启动中,视为幂等处理 |
|
|
return true |
|
|
return true |
|
|
@ -587,13 +619,13 @@ class AgentServiceImpl: NSObject { |
|
|
|
|
|
|
|
|
// 关键修复:如果正在结束的是通话模式,则立即将会话重置为 normal 状态 |
|
|
// 关键修复:如果正在结束的是通话模式,则立即将会话重置为 normal 状态 |
|
|
if self.currentRecognitionMode == "phone_call" { |
|
|
if self.currentRecognitionMode == "phone_call" { |
|
|
os_log("通话模式结束,强制重置音频会话至 'normal' 状态", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] phone_call mode ended, resetting audio session to normal") |
|
|
self.currentRecognitionMode = "normal" |
|
|
self.currentRecognitionMode = "normal" |
|
|
// 使用 normal 模式的配置来清理和重置音频会话 |
|
|
// 使用 normal 模式的配置来清理和重置音频会话 |
|
|
self.checkAdnSetAudioSession() |
|
|
self.checkAdnSetAudioSession() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
os_log("停止语音识别,当前模式: %{public}@", log: logger, type: .info, currentRecognitionMode) |
|
|
_log("INFO", "[Agent] stopRecognition, currentMode: \(currentRecognitionMode)") |
|
|
|
|
|
|
|
|
let success = azureAsrHelper?.stopContinuousRecognition() ?? false |
|
|
let success = azureAsrHelper?.stopContinuousRecognition() ?? false |
|
|
if success { |
|
|
if success { |
|
|
@ -601,9 +633,9 @@ class AgentServiceImpl: NSObject { |
|
|
// let audioSession = AVAudioSession.sharedInstance() |
|
|
// let audioSession = AVAudioSession.sharedInstance() |
|
|
// try audioSession.setCategory(.playback, mode: .spokenAudio, options: [.mixWithOthers, .allowBluetoothA2DP]) |
|
|
// try audioSession.setCategory(.playback, mode: .spokenAudio, options: [.mixWithOthers, .allowBluetoothA2DP]) |
|
|
// try audioSession.setActive(true) |
|
|
// try audioSession.setActive(true) |
|
|
os_log("Audio session switched to playback successfully after recognition.", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] audio session switched to playback after recognition") |
|
|
} catch { |
|
|
} catch { |
|
|
os_log("Failed to switch audio session to playback: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
_log("ERROR", "[Agent] failed to switch audio session to playback: \(error.localizedDescription)") |
|
|
} |
|
|
} |
|
|
} else if isStartingRecognition { |
|
|
} else if isStartingRecognition { |
|
|
// 启动尚未完成,先记录一次待停止请求,onSessionStarted 到来后立即 stop |
|
|
// 启动尚未完成,先记录一次待停止请求,onSessionStarted 到来后立即 stop |
|
|
@ -689,6 +721,8 @@ class AgentServiceImpl: NSObject { |
|
|
isAiStreaming = false |
|
|
isAiStreaming = false |
|
|
_ = chatApiService?.cancelCurrentStream() |
|
|
_ = chatApiService?.cancelCurrentStream() |
|
|
interrupted = true |
|
|
interrupted = true |
|
|
|
|
|
// AI流结束是重要的状态变化,重启idle检测避免识别状态卡住 |
|
|
|
|
|
restartIdleCheck() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if interrupted { |
|
|
if interrupted { |
|
|
@ -699,16 +733,16 @@ class AgentServiceImpl: NSObject { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func processTextInput(sessionid:String, _ text: String, speakResponse: Bool) -> Bool { |
|
|
func processTextInput(sessionid:String, _ text: String, speakResponse: Bool) -> Bool { |
|
|
os_log("收到文本输入: text=%{public}@, speakResponse=%{public}@", log: logger, type: .info, text, speakResponse ? "是" : "否") |
|
|
_log("INFO", "[Agent] processTextInput: text=\(text), speakResponse=\(speakResponse)") |
|
|
|
|
|
|
|
|
if !isInitialized { |
|
|
if !isInitialized { |
|
|
os_log("服务未初始化,无法处理文本输入", log: logger, type: .error) |
|
|
_log("ERROR", "[Agent] processTextInput: service not initialized") |
|
|
sendError("服务未初始化", code: "NOT_INITIALIZED") |
|
|
sendError("服务未初始化", code: "NOT_INITIALIZED") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if text.isEmpty { |
|
|
if text.isEmpty { |
|
|
os_log("文本输入为空", log: logger, type: .error) |
|
|
_log("ERROR", "[Agent] processTextInput: empty text") |
|
|
sendError("文本输入不能为空", code: "EMPTY_TEXT") |
|
|
sendError("文本输入不能为空", code: "EMPTY_TEXT") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
@ -735,17 +769,16 @@ class AgentServiceImpl: NSObject { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private func processWithChatApiServiceInternal(sessionid:String,userMessage: [String: Any], displayText: String, speakResponse: Bool = true, hasImage: Bool = false,imagePath:String = "") { |
|
|
private func processWithChatApiServiceInternal(sessionid:String,userMessage: [String: Any], displayText: String, speakResponse: Bool = true, hasImage: Bool = false,imagePath:String = "") { |
|
|
os_log("开始处理ChatAPI请求: displayText=%{public}@, speakResponse=%{public}@, hasImage=%{public}@", |
|
|
_log("INFO", "[Agent] ChatAPI request start: text=\(displayText), speak=\(speakResponse), hasImage=\(hasImage)") |
|
|
log: logger, type: .info, displayText, speakResponse ? "是" : "否", hasImage ? "是" : "否") |
|
|
|
|
|
|
|
|
|
|
|
guard let chatApiService = chatApiService else { |
|
|
guard let chatApiService = chatApiService else { |
|
|
os_log("ChatAPI服务未初始化", log: logger, type: .error) |
|
|
_log("ERROR", "[Agent] ChatAPI service not initialized") |
|
|
sendError("ChatAPI服务未初始化", code: "CHATAPI_NOT_INITIALIZED") |
|
|
sendError("ChatAPI服务未初始化", code: "CHATAPI_NOT_INITIALIZED") |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if isAiStreaming { |
|
|
if isAiStreaming { |
|
|
os_log("中断当前AI流式响应", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] interrupting current AI stream response") |
|
|
_ = interruptCurrentResponse() |
|
|
_ = interruptCurrentResponse() |
|
|
} |
|
|
} |
|
|
stopTts(); |
|
|
stopTts(); |
|
|
@ -759,10 +792,17 @@ class AgentServiceImpl: NSObject { |
|
|
var info = UserPortrait.shared.getAll() |
|
|
var info = UserPortrait.shared.getAll() |
|
|
info["language"] = self.language |
|
|
info["language"] = self.language |
|
|
|
|
|
|
|
|
// Add current time (formatted string) |
|
|
// Add current time (TTS友好的自然语言格式) |
|
|
|
|
|
let now = Date() |
|
|
|
|
|
let lang = Locale.current.languageCode ?? "en" |
|
|
let dateFormatter = DateFormatter() |
|
|
let dateFormatter = DateFormatter() |
|
|
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" |
|
|
switch lang { |
|
|
info["current_time"] = dateFormatter.string(from: Date()) |
|
|
case "zh": dateFormatter.dateFormat = "yyyy年M月d日 H时mm分ss秒"; dateFormatter.locale = Locale(identifier: "zh_CN") |
|
|
|
|
|
case "ja": dateFormatter.dateFormat = "yyyy年M月d日 H時mm分ss秒"; dateFormatter.locale = Locale(identifier: "ja_JP") |
|
|
|
|
|
case "ko": dateFormatter.dateFormat = "yyyy년 M월 d일 H시 mm분 ss초"; dateFormatter.locale = Locale(identifier: "ko_KR") |
|
|
|
|
|
default: dateFormatter.dateFormat = "MMMM d, yyyy, h:mm:ss a"; dateFormatter.locale = Locale.current |
|
|
|
|
|
} |
|
|
|
|
|
info["current_time"] = dateFormatter.string(from: now) |
|
|
// 添加调试日志 |
|
|
// 添加调试日志 |
|
|
// print("系统提示词模板: \(systemPrompt)") |
|
|
// print("系统提示词模板: \(systemPrompt)") |
|
|
// print("用户画像数据: \(info)") |
|
|
// print("用户画像数据: \(info)") |
|
|
@ -799,11 +839,11 @@ class AgentServiceImpl: NSObject { |
|
|
imagePath: imagePath |
|
|
imagePath: imagePath |
|
|
) |
|
|
) |
|
|
azureTtsHelper?.startspeak(sessionid: sessionid) |
|
|
azureTtsHelper?.startspeak(sessionid: sessionid) |
|
|
os_log("设置回调并调用sendMessageStream", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] sendMessageStream start, sessionId=\(sessionid)") |
|
|
chatApiService.setStreamCallback(sessionid,callback) |
|
|
chatApiService.setStreamCallback(sessionid,callback) |
|
|
chatApiService.sendMessageStream(sessionid,vipexptime: vipexptime,messages: messages) |
|
|
chatApiService.sendMessageStream(sessionid,vipexptime: vipexptime,messages: messages) |
|
|
audioPlayer?.playAwaitSound() |
|
|
audioPlayer?.playAwaitSound() |
|
|
os_log("sendMessageStream调用完成", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] sendMessageStream dispatched") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func processImageInput(sessionid: String,imagePath: String,imageUrl: String, text: String, speakResponse: Bool) -> Bool { |
|
|
func processImageInput(sessionid: String,imagePath: String,imageUrl: String, text: String, speakResponse: Bool) -> Bool { |
|
|
@ -905,7 +945,7 @@ class AgentServiceImpl: NSObject { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
os_log("已加载%d条历史记录", log: logger, type: .info, recentMessages.count) |
|
|
_log("INFO", "[Agent] loaded \(recentMessages.count) history messages") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -926,7 +966,7 @@ class AgentServiceImpl: NSObject { |
|
|
metadata: metadata.isEmpty ? nil : metadata |
|
|
metadata: metadata.isEmpty ? nil : metadata |
|
|
) |
|
|
) |
|
|
if assistantMessageId == -1 { |
|
|
if assistantMessageId == -1 { |
|
|
os_log("保存助手消息失败", log: self.logger, type: .error) |
|
|
self._log("ERROR", "[Agent] save assistant message failed") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// } else { |
|
|
// } else { |
|
|
@ -1058,7 +1098,7 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
let jsonData = try JSONSerialization.data(withJSONObject: json, options: []) |
|
|
let jsonData = try JSONSerialization.data(withJSONObject: json, options: []) |
|
|
return String(data: jsonData, encoding: .utf8) |
|
|
return String(data: jsonData, encoding: .utf8) |
|
|
} catch { |
|
|
} catch { |
|
|
print("JSON序列化失败: \(error)") |
|
|
_log("ERROR", "[Agent] JSON serialization failed: \(error)") |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -1495,7 +1535,7 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
DispatchQueue.global(qos: .utility).async { |
|
|
DispatchQueue.global(qos: .utility).async { |
|
|
let success = self.chatStorageHelper.deleteMessages(agentId:self.agentId, messageIds: nil as [Int]?) |
|
|
let success = self.chatStorageHelper.deleteMessages(agentId:self.agentId, messageIds: nil as [Int]?) |
|
|
if !success { |
|
|
if !success { |
|
|
os_log("清除聊天历史失败", log: self.logger, type: .error) |
|
|
self._log("ERROR", "[Agent] clear chat history failed") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1514,16 +1554,16 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
* @return 是否设置成功 |
|
|
* @return 是否设置成功 |
|
|
*/ |
|
|
*/ |
|
|
func setSupportedLanguages(_ languages: [String]) -> Bool { |
|
|
func setSupportedLanguages(_ languages: [String]) -> Bool { |
|
|
os_log("设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) |
|
|
_log("INFO", "[Agent] setSupportedLanguages: \(languages.joined(separator: ", "))") |
|
|
|
|
|
|
|
|
if !isInitialized { |
|
|
if !isInitialized { |
|
|
os_log("服务未初始化,无法设置语言", log: logger, type: .error) |
|
|
_log("ERROR", "[Agent] setSupportedLanguages: service not initialized") |
|
|
sendError("服务未初始化", code: "NOT_INITIALIZED") |
|
|
sendError("服务未初始化", code: "NOT_INITIALIZED") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if languages.isEmpty { |
|
|
if languages.isEmpty { |
|
|
os_log("语言列表为空,使用默认语言", log: logger, type: .info) |
|
|
_log("INFO", "[Agent] setSupportedLanguages: empty list, using default") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1563,9 +1603,9 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
let overallSuccess = asrSuccess && ttsSuccess |
|
|
let overallSuccess = asrSuccess && ttsSuccess |
|
|
|
|
|
|
|
|
if overallSuccess { |
|
|
if overallSuccess { |
|
|
os_log("ASR和TTS语言设置更新成功: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) |
|
|
_log("INFO", "[Agent] ASR and TTS language update success: \(languages.joined(separator: ", "))") |
|
|
} else { |
|
|
} else { |
|
|
os_log("语言设置更新失败 - ASR: %{public}@, TTS: %{public}@", log: logger, type: .error, String(asrSuccess), String(ttsSuccess)) |
|
|
_log("ERROR", "[Agent] language update failed - ASR: \(asrSuccess), TTS: \(ttsSuccess)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 如果之前正在识别,重新开始 |
|
|
// 如果之前正在识别,重新开始 |
|
|
@ -1583,7 +1623,7 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
* @return 是否更新成功 |
|
|
* @return 是否更新成功 |
|
|
*/ |
|
|
*/ |
|
|
private func updateTtsLanguage(_ languageCode: String) -> Bool { |
|
|
private func updateTtsLanguage(_ languageCode: String) -> Bool { |
|
|
os_log("更新TTS语言设置: %{public}@", log: logger, type: .info, languageCode) |
|
|
_log("INFO", "[Agent] updateTtsLanguage: \(languageCode)") |
|
|
|
|
|
|
|
|
// 根据ASR语言代码映射到对应的TTS语音角色 |
|
|
// 根据ASR语言代码映射到对应的TTS语音角色 |
|
|
let voiceName = getTtsVoiceNameByAsrCode(languageCode) |
|
|
let voiceName = getTtsVoiceNameByAsrCode(languageCode) |
|
|
@ -1593,14 +1633,14 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
let success = azureTtsHelper?.setVoice(voiceName) ?? false |
|
|
let success = azureTtsHelper?.setVoice(voiceName) ?? false |
|
|
|
|
|
|
|
|
if success { |
|
|
if success { |
|
|
os_log("TTS语音角色设置成功: %{public}@", log: logger, type: .info, voiceName) |
|
|
_log("INFO", "[Agent] TTS voice set success: \(voiceName)") |
|
|
} else { |
|
|
} else { |
|
|
os_log("TTS语音角色设置失败: %{public}@", log: logger, type: .error, voiceName) |
|
|
_log("ERROR", "[Agent] TTS voice set failed: \(voiceName)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return success |
|
|
return success |
|
|
} else { |
|
|
} else { |
|
|
os_log("未找到语言代码 %{public}@ 对应的TTS语音角色", log: logger, type: .error, languageCode) |
|
|
_log("ERROR", "[Agent] TTS voice not found for language: \(languageCode)") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -1731,13 +1771,13 @@ private func jsonToString(_ json: [String: Any]) -> String? { |
|
|
return "fil-PH-BlessicaNeural" |
|
|
return "fil-PH-BlessicaNeural" |
|
|
|
|
|
|
|
|
default: |
|
|
default: |
|
|
os_log("未支持的语言代码: %{public}@,使用默认英语语音", log: logger, type: .error, asrCode) |
|
|
_log("WARNING", "[Agent] unsupported language code: \(asrCode), using default en-US voice") |
|
|
return "en-US-AriaNeural" |
|
|
return "en-US-AriaNeural" |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func dispose() -> Bool { |
|
|
func dispose() -> Bool { |
|
|
print("ai释放资源") |
|
|
_log("INFO", "[Agent] dispose") |
|
|
if isRecognizing { |
|
|
if isRecognizing { |
|
|
// 关闭编码器 (设备侧) |
|
|
// 关闭编码器 (设备侧) |
|
|
BleService.shared.closeCodec() |
|
|
BleService.shared.closeCodec() |
|
|
@ -1823,7 +1863,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
try agentService.sendEvent(name: "assistant_token", data: ["sessionid":sessionid,"token": token]) |
|
|
try agentService.sendEvent(name: "assistant_token", data: ["sessionid":sessionid,"token": token]) |
|
|
} |
|
|
} |
|
|
}catch { |
|
|
}catch { |
|
|
print("liwei--------------- AI Call onToken 异常: \(error)") |
|
|
agentService?._log("ERROR", "[Agent] AI onToken error: \(error)") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1835,7 +1875,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
// 停止等待音效 |
|
|
// 停止等待音效 |
|
|
agentService.audioPlayer?.stopAwaitSound() |
|
|
agentService.audioPlayer?.stopAwaitSound() |
|
|
} |
|
|
} |
|
|
os_log("onComplete agentService.isInterrupt:%{public}@", log: agentService.logger, type: .info, String(agentService.isInterrupt)) |
|
|
agentService._log("INFO", "[Agent] AI onComplete, isInterrupt=\(agentService.isInterrupt)") |
|
|
if(agentService.isInterrupt){ |
|
|
if(agentService.isInterrupt){ |
|
|
BleService.shared.closeCodec() |
|
|
BleService.shared.closeCodec() |
|
|
} |
|
|
} |
|
|
@ -1870,9 +1910,9 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
try agentService.saveChatMessage(sessionId: sessionid,userMessage: displayText, assistantMessage: response, metadata: metadata,userMetadata:toJSONString(userMetadata)) |
|
|
try agentService.saveChatMessage(sessionId: sessionid,userMessage: displayText, assistantMessage: response, metadata: metadata,userMetadata:toJSONString(userMetadata)) |
|
|
agentService.isAiStreaming = false |
|
|
agentService.isAiStreaming = false |
|
|
iscallingTool = true |
|
|
iscallingTool = true |
|
|
os_log("设置AI流式状态为false", log: agentService.logger, type: .info) |
|
|
agentService._log("INFO", "[Agent] AI stream completed, response=\(response)") |
|
|
}catch{ |
|
|
}catch{ |
|
|
print("liwei--------------- AI Call onComplete 异常: \(error)") |
|
|
agentService?._log("ERROR", "[Agent] AI onComplete error: \(error)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
@ -1897,15 +1937,9 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
agentService.azureTtsHelper?.speakStream(sessionid: sessionid,agentService.insufficientIntegralText) |
|
|
agentService.azureTtsHelper?.speakStream(sessionid: sessionid,agentService.insufficientIntegralText) |
|
|
} |
|
|
} |
|
|
agentService.isAiStreaming = false |
|
|
agentService.isAiStreaming = false |
|
|
os_log( |
|
|
agentService._log("ERROR", "[Agent] AI onError code=\(code), message=\(message)") |
|
|
"liwei--------------- AI Call onError code: %{public}d message: %{public}@ ", |
|
|
|
|
|
log: agentService.logger, |
|
|
|
|
|
type: .info, |
|
|
|
|
|
code, // 直接传 Int 类型 |
|
|
|
|
|
message // 假设 message 是 String 类型 |
|
|
|
|
|
) |
|
|
|
|
|
}catch{ |
|
|
}catch{ |
|
|
print("liwei--------------- AI Call onError 异常: \(error)") |
|
|
agentService?._log("ERROR", "[Agent] AI onError exception: \(error)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
@ -1918,8 +1952,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
} |
|
|
} |
|
|
let name = functionCall["name"] as? String ?? "" |
|
|
let name = functionCall["name"] as? String ?? "" |
|
|
let arguments = functionCall["arguments"] as? String ?? "" |
|
|
let arguments = functionCall["arguments"] as? String ?? "" |
|
|
os_log("收到函数调用:%{public}@", log: agentService.logger, type: .info, functionCall) |
|
|
agentService._log("INFO", "[Agent] AI onFunctionCall: name=\(name), args=\(arguments)") |
|
|
print("onFunctionCall=functionCall\(functionCall)") |
|
|
|
|
|
|
|
|
|
|
|
if (name == "exit_interaction") { |
|
|
if (name == "exit_interaction") { |
|
|
// 关闭编码器 (设备侧) |
|
|
// 关闭编码器 (设备侧) |
|
|
@ -1936,7 +1969,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
]) |
|
|
]) |
|
|
|
|
|
|
|
|
if (iscallingTool) { |
|
|
if (iscallingTool) { |
|
|
os_log("收到函数调用: callingToolText:%{public}@", log: agentService.logger, type: .info, agentService.callingToolText) |
|
|
agentService._log("INFO", "[Agent] AI onFunctionCall: callingToolText=\(agentService.callingToolText)") |
|
|
// _ = agentService.azureTtsHelper?.setTtsMode(mod: agentService.currentRecognitionMode) |
|
|
// _ = agentService.azureTtsHelper?.setTtsMode(mod: agentService.currentRecognitionMode) |
|
|
agentService.azureTtsHelper?.speakStream(sessionid: sessionid,agentService.callingToolText) |
|
|
agentService.azureTtsHelper?.speakStream(sessionid: sessionid,agentService.callingToolText) |
|
|
iscallingTool = false |
|
|
iscallingTool = false |
|
|
@ -1944,7 +1977,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}catch{ |
|
|
}catch{ |
|
|
print("liwei--------------- AI Call onFunctionCall 异常: \(error)") |
|
|
agentService?._log("ERROR", "[Agent] AI onFunctionCall error: \(error)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
@ -1961,10 +1994,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
broadcast = resultmap["broadcast"] as? Bool ?? true |
|
|
broadcast = resultmap["broadcast"] as? Bool ?? true |
|
|
reply = !(resultmap["notreply"] as? Bool ?? true) |
|
|
reply = !(resultmap["notreply"] as? Bool ?? true) |
|
|
iscard = resultmap["iscard"] as? Bool ?? false |
|
|
iscard = resultmap["iscard"] as? Bool ?? false |
|
|
os_log("收到函数调用结果! broadcast: %{public}@ reply: %{public}@ %{iscard}@", |
|
|
agentService._log("INFO", "[Agent] AI onFunctionCallResult: name=\(name), broadcast=\(broadcast), reply=\(reply), iscard=\(iscard)") |
|
|
log: agentService.logger, |
|
|
|
|
|
type: .info, |
|
|
|
|
|
"\(broadcast)", "\(reply)", "\(iscard)") |
|
|
|
|
|
if (iscard){ |
|
|
if (iscard){ |
|
|
metadata = resultmap["meta"] as? String ?? "" |
|
|
metadata = resultmap["meta"] as? String ?? "" |
|
|
agentService.sendEvent(name: "function_call_result", data: [ |
|
|
agentService.sendEvent(name: "function_call_result", data: [ |
|
|
@ -1984,7 +2014,7 @@ class ChatApiStreamCallback: StreamCallback { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
}catch{ |
|
|
}catch{ |
|
|
print("liwei--------------- AI Call onFunctionCallResult 异常: \(error)") |
|
|
agentService?._log("ERROR", "[Agent] AI onFunctionCallResult error: \(error)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
@ -2024,8 +2054,8 @@ extension AgentServiceImpl: TtsEventListener { |
|
|
case .playbackCompleted: |
|
|
case .playbackCompleted: |
|
|
isSpeaking = false |
|
|
isSpeaking = false |
|
|
restartIdleCheck() |
|
|
restartIdleCheck() |
|
|
if(isInterrupt){//播报结束。打开解码器 |
|
|
if(currentRecognitionMode == "ble_wakeup" && isInterrupt){//播报结束。打开解码器 |
|
|
BleService.shared.openB1Encoder() |
|
|
BleService.shared.openB1Encoder() |
|
|
} |
|
|
} |
|
|
sendEvent(name: "playback_completed", data: ["status": "playback_completed"]) |
|
|
sendEvent(name: "playback_completed", data: ["status": "playback_completed"]) |
|
|
MusicService.shared.restoreVolume() |
|
|
MusicService.shared.restoreVolume() |
|
|
@ -2377,13 +2407,10 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { |
|
|
func onRecognizing(sessionid:String ,_ recognizing: String, _ detectedLanguage: String) { |
|
|
func onRecognizing(sessionid:String ,_ recognizing: String, _ detectedLanguage: String) { |
|
|
pendingProcessWorkItem?.cancel() |
|
|
pendingProcessWorkItem?.cancel() |
|
|
if !recognizing.isEmpty && isKeepResult { |
|
|
if !recognizing.isEmpty && isKeepResult { |
|
|
let previousHasSpeech = hasSpeechDetected |
|
|
|
|
|
hasSpeechDetected = true |
|
|
hasSpeechDetected = true |
|
|
|
|
|
// 每次识别到语音都重启idle计时器,避免用户持续说话时误触超时 |
|
|
|
|
|
restartIdleCheck() |
|
|
|
|
|
|
|
|
if !previousHasSpeech { |
|
|
|
|
|
print("onRecognizing restartIdleCheck") |
|
|
|
|
|
restartIdleCheck() |
|
|
|
|
|
} |
|
|
|
|
|
if currsessionAsrId.isEmpty { |
|
|
if currsessionAsrId.isEmpty { |
|
|
currsessionAsrId = sessionid |
|
|
currsessionAsrId = sessionid |
|
|
} |
|
|
} |
|
|
@ -2397,9 +2424,13 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { |
|
|
} |
|
|
} |
|
|
sendEvent(name: "recognizing", data: data) |
|
|
sendEvent(name: "recognizing", data: data) |
|
|
|
|
|
|
|
|
|
|
|
// 执行打断:同时停止TTS和AI流,避免isAiStreaming残留导致idle check失效 |
|
|
if isSpeaking || isAiStreaming { |
|
|
if isSpeaking || isAiStreaming { |
|
|
stopTts() |
|
|
stopTts() |
|
|
// interruptCurrentResponse() |
|
|
if isAiStreaming { |
|
|
|
|
|
isAiStreaming = false |
|
|
|
|
|
_ = chatApiService?.cancelCurrentStream() |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -2504,7 +2535,7 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { |
|
|
let userDefaults = UserDefaults.standard |
|
|
let userDefaults = UserDefaults.standard |
|
|
let cachedValue = userDefaults.bool(forKey: KEY_IS_INTERRUPT) // 默认值为 false |
|
|
let cachedValue = userDefaults.bool(forKey: KEY_IS_INTERRUPT) // 默认值为 false |
|
|
isInterrupt = cachedValue |
|
|
isInterrupt = cachedValue |
|
|
os_log("从缓存加载 isInterrupt 状态: %{public}@", log: logger, type: .info, String(isInterrupt)) |
|
|
_log("INFO", "[Agent] loaded isInterrupt from cache: \(isInterrupt)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -2513,19 +2544,19 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { |
|
|
func saveIsInterruptToCache(_ isInterrupt: Bool) { |
|
|
func saveIsInterruptToCache(_ isInterrupt: Bool) { |
|
|
let userDefaults = UserDefaults.standard |
|
|
let userDefaults = UserDefaults.standard |
|
|
userDefaults.set(isInterrupt, forKey: KEY_IS_INTERRUPT) |
|
|
userDefaults.set(isInterrupt, forKey: KEY_IS_INTERRUPT) |
|
|
os_log("保存 isInterrupt 状态到缓存: %{public}@", log: logger, type: .info, String(isInterrupt)) |
|
|
_log("INFO", "[Agent] saved isInterrupt to cache: \(isInterrupt)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private func loadTtsRateFromCache() { |
|
|
private func loadTtsRateFromCache() { |
|
|
let userDefaults = UserDefaults.standard |
|
|
let userDefaults = UserDefaults.standard |
|
|
let cachedValue = userDefaults.object(forKey: KEY_TTS_RATE) as? Int ?? 25 |
|
|
let cachedValue = userDefaults.object(forKey: KEY_TTS_RATE) as? Int ?? 25 |
|
|
ttsRatePercent = max(-100, min(100, cachedValue)) |
|
|
ttsRatePercent = max(-100, min(100, cachedValue)) |
|
|
os_log("从缓存加载 ttsRatePercent: %{public}@", log: logger, type: .info, String(ttsRatePercent)) |
|
|
_log("INFO", "[Agent] loaded ttsRatePercent from cache: \(ttsRatePercent)") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private func saveTtsRateToCache(_ rate: Int) { |
|
|
private func saveTtsRateToCache(_ rate: Int) { |
|
|
let userDefaults = UserDefaults.standard |
|
|
let userDefaults = UserDefaults.standard |
|
|
userDefaults.set(rate, forKey: KEY_TTS_RATE) |
|
|
userDefaults.set(rate, forKey: KEY_TTS_RATE) |
|
|
os_log("保存 ttsRatePercent 到缓存: %{public}@", log: logger, type: .info, String(rate)) |
|
|
_log("INFO", "[Agent] saved ttsRatePercent to cache: \(rate)") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|