From 303bc6972c97d6395c7e93e4b52db816cd960b5b Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Tue, 31 Mar 2026 14:43:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=97=B6=E9=97=B4=E6=92=AD?= =?UTF-8?q?=E6=8A=A5=E6=88=90=E6=95=B0=E5=AD=97=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_service/AgentService.kt | 59 +++++----- .../agent_service/AgentServiceImpl.swift | 70 +++++++----- .../chat_api/SystemFunctionHandler.kt | 86 +++++++++------ .../chat_api/SystemFunctionHandler.swift | 104 ++++++++++++++++-- .../open_ai_service/SystemFunctionHandler.kt | 93 +++++++++------- .../ios/Classes/SystemFunctionHandler.swift | 103 +++++++++++++++-- .../SystemFunctionHandler.swift | 103 +++++++++++++++-- 7 files changed, 454 insertions(+), 164 deletions(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index c5d38c78c..b08534a5e 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -597,28 +597,26 @@ object AgentService : CoroutineScope { idleCheckJob = launch { try { delay(idleSeconds * 1000L) - Log.d(TAG, "空闲检测触发自动停止,isActive: $isActive, isRecognitionActive: $isRecognitionActive, isTtsSpeaking: $isTtsSpeaking, isAiStreaming: $isAiStreaming, mode: $mode") - if (isActive && isRecognitionActive && !isTtsSpeaking && !isAiStreaming) { - Log.d(TAG, "空闲检测触发自动停止,模式: $currentRecognitionMode") - BleService.closeCodec() - if (currentRecognitionMode != "phone_call"){ - stopRecognition() - } - sendEvent( - "recognition_result", mapOf( - "sessionid" to currsessionAsrId, - "text" to recognitionResult, - // "language" to detectedLanguage + Log.d(TAG, "空闲检测触发,isActive: $isActive, isRecognitionActive: $isRecognitionActive, isTtsSpeaking: $isTtsSpeaking, isAiStreaming: $isAiStreaming, mode: $mode") + if (isActive && isRecognitionActive) { + if (!isTtsSpeaking && !isAiStreaming) { + // 条件满足:TTS未播放且AI未流式输出,执行自动停止 + Log.d(TAG, "空闲检测触发自动停止,模式: $currentRecognitionMode") + BleService.closeCodec() + if (currentRecognitionMode != "phone_call"){ + stopRecognition() + } + sendEvent( + "recognition_result", mapOf( + "sessionid" to currsessionAsrId, + "text" to recognitionResult, + ) ) - ) - // sendEvent( - // "auto_stop", - // mapOf( - // "reason" to "idle_timeout", - // "seconds" to idleSeconds, - // "mode" to currentRecognitionMode - // ) - // ) + } else { + // 条件不满足:TTS或AI仍在工作中,重新调度计时器等待下一轮检查 + Log.d(TAG, "空闲检测条件不满足(tts=$isTtsSpeaking, ai=$isAiStreaming),重新调度") + startIdleCheck(mode, idleSeconds) + } } } catch (e: CancellationException) { // 协程被取消,正常行为 @@ -775,10 +773,10 @@ object AgentService : CoroutineScope { return } - // 执行打断 + // 执行打断:同时停止TTS和AI流,避免isAiStreaming残留导致idle check失效 lastInterruptTime = currentTime stopTts() - // interruptCurrentResponse() + stopAiStream() } } } @@ -1002,11 +1000,15 @@ object AgentService : CoroutineScope { //结束等待音 audioPlayer?.stopAudio() + // AI流结束是重要的状态变化,重启idle检测避免识别状态卡住 + restartIdleCheck() + } catch (e: Exception) { _log("ERROR", "[Agent] 停止AI流异常: ${e.message}") // 确保状态被重置,即使发生异常 _isAiStreaming.set(false) currentAiJob = null + restartIdleCheck() } } } @@ -1130,8 +1132,15 @@ object AgentService : CoroutineScope { if (systemPrompt.isNotEmpty()) { val mapdata = UserPortrait.getAll().toMutableMap() mapdata["language"] = language - // 添加当前时间(格式化字符串) - val currentTimeFormatted = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()) + // 添加当前时间(TTS友好的自然语言格式) + val lang = Locale.getDefault().language + val now = Date() + val currentTimeFormatted = when (lang) { + "zh" -> SimpleDateFormat("yyyy年M月d日 H时mm分ss秒", java.util.Locale.CHINESE).format(now) + "ja" -> SimpleDateFormat("yyyy年M月d日 H時mm分ss秒", java.util.Locale.JAPANESE).format(now) + "ko" -> SimpleDateFormat("yyyy년 M월 d일 H시 mm분 ss초", java.util.Locale.KOREAN).format(now) + else -> SimpleDateFormat("MMMM d, yyyy, h:mm:ss a", Locale.getDefault()).format(now) + } mapdata["current_time"] = currentTimeFormatted // Log.d(TAG, "liwei--------- 用户画像: ${mapdata}") val _systemPrompt = Utils.fillTemplate(systemPrompt, mapdata) diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index 836a69fb0..af68fa34f 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -482,18 +482,24 @@ class AgentServiceImpl: NSObject { guard let self = self else { return } DispatchQueue.main.async { [weak self] in guard let self = self else { return } - self._log("INFO", "[Agent] idle check triggered auto stop, mode: \(self.currentRecognitionMode)") - //打电话模式下面 不能关闭识别器 - if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming{ - BleService.shared.closeCodec() - if (currentRecognitionMode != "phone_call"){ - self.stopRecognition() + self._log("INFO", "[Agent] idle check triggered, mode: \(self.currentRecognitionMode), speaking: \(self.isSpeaking), aiStreaming: \(self.isAiStreaming)") + if self.isRecognizing { + if !self.isSpeaking && !self.isAiStreaming { + // 条件满足:TTS未播放且AI未流式输出,执行自动停止 + BleService.shared.closeCodec() + if (self.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 - ]) } } } @@ -706,17 +712,19 @@ class AgentServiceImpl: NSObject { func interruptCurrentResponse() -> Bool { var interrupted = false - + if isSpeaking { interrupted = stopTts() || interrupted } - + if isAiStreaming { isAiStreaming = false _ = chatApiService?.cancelCurrentStream() interrupted = true + // AI流结束是重要的状态变化,重启idle检测避免识别状态卡住 + restartIdleCheck() } - + if interrupted { sendEvent(name: "response_interrupted", data: ["status": "interrupted"]) } @@ -784,10 +792,17 @@ class AgentServiceImpl: NSObject { var info = UserPortrait.shared.getAll() 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() - dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - info["current_time"] = dateFormatter.string(from: Date()) + switch lang { + 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("用户画像数据: \(info)") @@ -2039,8 +2054,8 @@ extension AgentServiceImpl: TtsEventListener { case .playbackCompleted: isSpeaking = false restartIdleCheck() - if(isInterrupt){//播报结束。打开解码器 - BleService.shared.openB1Encoder() + if(currentRecognitionMode == "ble_wakeup" && isInterrupt){//播报结束。打开解码器 + BleService.shared.openB1Encoder() } sendEvent(name: "playback_completed", data: ["status": "playback_completed"]) MusicService.shared.restoreVolume() @@ -2392,13 +2407,10 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { func onRecognizing(sessionid:String ,_ recognizing: String, _ detectedLanguage: String) { pendingProcessWorkItem?.cancel() if !recognizing.isEmpty && isKeepResult { - let previousHasSpeech = hasSpeechDetected hasSpeechDetected = true - - if !previousHasSpeech { - print("onRecognizing restartIdleCheck") - restartIdleCheck() - } + // 每次识别到语音都重启idle计时器,避免用户持续说话时误触超时 + restartIdleCheck() + if currsessionAsrId.isEmpty { currsessionAsrId = sessionid } @@ -2411,10 +2423,14 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { data["language"] = detectedLanguage } sendEvent(name: "recognizing", data: data) - + + // 执行打断:同时停止TTS和AI流,避免isAiStreaming残留导致idle check失效 if isSpeaking || isAiStreaming { stopTts() - // interruptCurrentResponse() + if isAiStreaming { + isAiStreaming = false + _ = chatApiService?.cancelCurrentStream() + } } } } diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt index 0eb96287f..588beb412 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt @@ -1010,51 +1010,31 @@ private class SetReminderHandler(private val context: Context?) : FunctionHandle private class GetCurrentTimeHandler : FunctionHandler { override suspend fun handle(arguments: Map): String { val format = arguments["format"] as? String - + return try { val calendar = Calendar.getInstance() val date = calendar.time - + val lang = Locale.getDefault().language + val timeStr = when (format?.lowercase(Locale.getDefault())) { - "short" -> { - SimpleDateFormat("HH:mm", Locale.getDefault()).format(date) - } - "date" -> { - SimpleDateFormat("yyyy年MM月dd日", Locale.getDefault()).format(date) - } - "time" -> { - SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(date) - } - "full" -> { - SimpleDateFormat("yyyy年MM月dd日 EEEE HH:mm:ss", Locale.getDefault()).format(date) - } - "iso" -> { - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()).format(date) - } - else -> { - SimpleDateFormat("yyyy年MM月dd日 HH:mm", Locale.getDefault()).format(date) - } + "short" -> formatShortTime(date, lang) + "date" -> formatDate(date, lang) + "time" -> formatTime(date, lang) + "full" -> formatFull(date, lang) + "iso" -> SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()).format(date) + else -> formatDefault(date, lang) } - - // 获取星期几 - val weekDay = when (calendar.get(Calendar.DAY_OF_WEEK)) { - Calendar.SUNDAY -> "星期日" - Calendar.MONDAY -> "星期一" - Calendar.TUESDAY -> "星期二" - Calendar.WEDNESDAY -> "星期三" - Calendar.THURSDAY -> "星期四" - Calendar.FRIDAY -> "星期五" - Calendar.SATURDAY -> "星期六" - else -> "" - } - + + // 获取星期几(使用系统locale自动适配语言) + val weekDay = SimpleDateFormat("EEEE", Locale.getDefault()).format(date) + // 根据格式决定是否添加星期几 val resultTime = when { - format == "short" || format == "time" -> timeStr + format == "short" || format == "time" || format == "iso" -> timeStr format == "full" -> timeStr // full格式已经包含了星期 else -> "$timeStr $weekDay" } - + JSONObject().apply { put("result", resultTime) put("time", resultTime) @@ -1069,6 +1049,42 @@ private class GetCurrentTimeHandler : FunctionHandler { }.toString() } } + + // TTS友好的时间格式,避免AI将":"分隔的时间误读为数字 + private fun formatShortTime(date: Date, lang: String): String = when (lang) { + "zh" -> SimpleDateFormat("H时mm分", Locale.CHINESE).format(date) + "ja" -> SimpleDateFormat("H時mm分", Locale.JAPANESE).format(date) + "ko" -> SimpleDateFormat("H시 mm분", Locale.KOREAN).format(date) + else -> SimpleDateFormat("h:mm a", Locale.getDefault()).format(date) + } + + private fun formatDate(date: Date, lang: String): String = when (lang) { + "zh" -> SimpleDateFormat("yyyy年M月d日", Locale.CHINESE).format(date) + "ja" -> SimpleDateFormat("yyyy年M月d日", Locale.JAPANESE).format(date) + "ko" -> SimpleDateFormat("yyyy년 M월 d일", Locale.KOREAN).format(date) + else -> SimpleDateFormat("MMMM d, yyyy", Locale.getDefault()).format(date) + } + + private fun formatTime(date: Date, lang: String): String = when (lang) { + "zh" -> SimpleDateFormat("H时mm分ss秒", Locale.CHINESE).format(date) + "ja" -> SimpleDateFormat("H時mm分ss秒", Locale.JAPANESE).format(date) + "ko" -> SimpleDateFormat("H시 mm분 ss초", Locale.KOREAN).format(date) + else -> SimpleDateFormat("h:mm:ss a", Locale.getDefault()).format(date) + } + + private fun formatFull(date: Date, lang: String): String = when (lang) { + "zh" -> SimpleDateFormat("yyyy年M月d日 EEEE H时mm分ss秒", Locale.CHINESE).format(date) + "ja" -> SimpleDateFormat("yyyy年M月d日 EEEE H時mm分ss秒", Locale.JAPANESE).format(date) + "ko" -> SimpleDateFormat("yyyy년 M월 d일 EEEE H시 mm분 ss초", Locale.KOREAN).format(date) + else -> SimpleDateFormat("MMMM d, yyyy, EEEE, h:mm:ss a", Locale.getDefault()).format(date) + } + + private fun formatDefault(date: Date, lang: String): String = when (lang) { + "zh" -> SimpleDateFormat("yyyy年M月d日 H时mm分", Locale.CHINESE).format(date) + "ja" -> SimpleDateFormat("yyyy年M月d日 H時mm分", Locale.JAPANESE).format(date) + "ko" -> SimpleDateFormat("yyyy년 M월 d일 H시 mm분", Locale.KOREAN).format(date) + else -> SimpleDateFormat("MMMM d, yyyy, h:mm a", Locale.getDefault()).format(date) + } } /** diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift index 9112f82f8..8d974dded 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift @@ -866,20 +866,100 @@ private class GetCurrentTimeHandler: FunctionHandler { let result = getCurrentTime(format: format) return "{\"result\": \"\(result.message)\", \"time\": \"\(result.message)\"}" } - + private func getCurrentTime(format: String?) -> Result { - let formatter = DateFormatter() - - if let customFormat = format, !customFormat.isEmpty { - formatter.dateFormat = customFormat - } else { - formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + let now = Date() + let lang = Locale.current.languageCode ?? "en" + + let timeStr: String + switch format?.lowercased() { + case "short": + timeStr = formatShortTime(now, lang: lang) + case "date": + timeStr = formatDate(now, lang: lang) + case "time": + timeStr = formatTime(now, lang: lang) + case "full": + timeStr = formatFull(now, lang: lang) + case "iso": + let f = DateFormatter() + f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + timeStr = f.string(from: now) + default: + timeStr = formatDefault(now, lang: lang) } - - formatter.locale = Locale(identifier: "zh_CN") - let timeString = formatter.string(from: Date()) - - return Result(success: true, message: timeString) + + // 根据格式决定是否添加星期几 + let resultTime: String + switch format?.lowercased() { + case "short", "time", "iso": + resultTime = timeStr + case "full": + resultTime = timeStr // full格式已包含星期 + default: + let weekFormatter = DateFormatter() + weekFormatter.dateFormat = "EEEE" + weekFormatter.locale = Locale.current + resultTime = "\(timeStr) \(weekFormatter.string(from: now))" + } + + return Result(success: true, message: resultTime) + } + + // TTS友好的时间格式,避免AI将":"分隔的时间误读为数字 + private func formatShortTime(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "H时mm分"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "H時mm分"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "H시 mm분"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "h:mm a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatDate(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatTime(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "h:mm:ss a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatFull(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日 EEEE H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日 EEEE H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일 EEEE H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy, EEEE, h:mm:ss a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatDefault(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日 H时mm分"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日 H時mm分"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일 H시 mm분"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy, h:mm a"; f.locale = Locale.current + } + return f.string(from: date) } } diff --git a/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt b/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt index 845b60d23..b9adae914 100644 --- a/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt +++ b/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt @@ -1060,49 +1060,24 @@ class SystemFunctionHandler(private val context: Context) { try { val calendar = java.util.Calendar.getInstance() val date = calendar.time - + val lang = java.util.Locale.getDefault().language + + // TTS友好的时间格式,避免AI将":"分隔的时间误读为数字 val timeStr = when (format?.lowercase(java.util.Locale.getDefault())) { - "short" -> { - val timeFormat = java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault()) - timeFormat.format(date) - } - "date" -> { - val dateFormat = java.text.SimpleDateFormat("yyyy年MM月dd日", java.util.Locale.getDefault()) - dateFormat.format(date) - } - "time" -> { - val timeFormat = java.text.SimpleDateFormat("HH:mm:ss", java.util.Locale.getDefault()) - timeFormat.format(date) - } - "full" -> { - val fullFormat = java.text.SimpleDateFormat("yyyy年MM月dd日 EEEE HH:mm:ss", java.util.Locale.getDefault()) - fullFormat.format(date) - } - "iso" -> { - val isoFormat = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", java.util.Locale.getDefault()) - isoFormat.format(date) - } - else -> { - val defaultFormat = java.text.SimpleDateFormat("yyyy年MM月dd日 HH:mm", java.util.Locale.getDefault()) - defaultFormat.format(date) - } - } - - // 获取星期几 - val weekDay = when (calendar.get(java.util.Calendar.DAY_OF_WEEK)) { - java.util.Calendar.SUNDAY -> "星期日" - java.util.Calendar.MONDAY -> "星期一" - java.util.Calendar.TUESDAY -> "星期二" - java.util.Calendar.WEDNESDAY -> "星期三" - java.util.Calendar.THURSDAY -> "星期四" - java.util.Calendar.FRIDAY -> "星期五" - java.util.Calendar.SATURDAY -> "星期六" - else -> "" + "short" -> formatShortTime(date, lang) + "date" -> formatDate(date, lang) + "time" -> formatTime(date, lang) + "full" -> formatFull(date, lang) + "iso" -> java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", java.util.Locale.getDefault()).format(date) + else -> formatDefaultTime(date, lang) } - - // 如果不是返回短格式,则添加星期几(除非format已经包含星期) + + // 获取星期几(使用系统locale自动适配语言) + val weekDay = java.text.SimpleDateFormat("EEEE", java.util.Locale.getDefault()).format(date) + + // 根据格式决定是否添加星期几 val resultTime = when { - format == "short" || format == "time" -> timeStr + format == "short" || format == "time" || format == "iso" -> timeStr format == "full" -> timeStr // full格式已经包含了星期 else -> "$timeStr $weekDay" } @@ -1120,7 +1095,43 @@ class SystemFunctionHandler(private val context: Context) { ) } } - + + // TTS友好的时间格式辅助方法 + private fun formatShortTime(date: java.util.Date, lang: String): String = when (lang) { + "zh" -> java.text.SimpleDateFormat("H时mm分", java.util.Locale.CHINESE).format(date) + "ja" -> java.text.SimpleDateFormat("H時mm分", java.util.Locale.JAPANESE).format(date) + "ko" -> java.text.SimpleDateFormat("H시 mm분", java.util.Locale.KOREAN).format(date) + else -> java.text.SimpleDateFormat("h:mm a", java.util.Locale.getDefault()).format(date) + } + + private fun formatDate(date: java.util.Date, lang: String): String = when (lang) { + "zh" -> java.text.SimpleDateFormat("yyyy年M月d日", java.util.Locale.CHINESE).format(date) + "ja" -> java.text.SimpleDateFormat("yyyy年M月d日", java.util.Locale.JAPANESE).format(date) + "ko" -> java.text.SimpleDateFormat("yyyy년 M월 d일", java.util.Locale.KOREAN).format(date) + else -> java.text.SimpleDateFormat("MMMM d, yyyy", java.util.Locale.getDefault()).format(date) + } + + private fun formatTime(date: java.util.Date, lang: String): String = when (lang) { + "zh" -> java.text.SimpleDateFormat("H时mm分ss秒", java.util.Locale.CHINESE).format(date) + "ja" -> java.text.SimpleDateFormat("H時mm分ss秒", java.util.Locale.JAPANESE).format(date) + "ko" -> java.text.SimpleDateFormat("H시 mm분 ss초", java.util.Locale.KOREAN).format(date) + else -> java.text.SimpleDateFormat("h:mm:ss a", java.util.Locale.getDefault()).format(date) + } + + private fun formatFull(date: java.util.Date, lang: String): String = when (lang) { + "zh" -> java.text.SimpleDateFormat("yyyy年M月d日 EEEE H时mm分ss秒", java.util.Locale.CHINESE).format(date) + "ja" -> java.text.SimpleDateFormat("yyyy年M月d日 EEEE H時mm分ss秒", java.util.Locale.JAPANESE).format(date) + "ko" -> java.text.SimpleDateFormat("yyyy년 M월 d일 EEEE H시 mm분 ss초", java.util.Locale.KOREAN).format(date) + else -> java.text.SimpleDateFormat("MMMM d, yyyy, EEEE, h:mm:ss a", java.util.Locale.getDefault()).format(date) + } + + private fun formatDefaultTime(date: java.util.Date, lang: String): String = when (lang) { + "zh" -> java.text.SimpleDateFormat("yyyy年M月d日 H时mm分", java.util.Locale.CHINESE).format(date) + "ja" -> java.text.SimpleDateFormat("yyyy年M月d日 H時mm分", java.util.Locale.JAPANESE).format(date) + "ko" -> java.text.SimpleDateFormat("yyyy년 M월 d일 H시 mm분", java.util.Locale.KOREAN).format(date) + else -> java.text.SimpleDateFormat("MMMM d, yyyy, h:mm a", java.util.Locale.getDefault()).format(date) + } + /** * 获取当前位置(简化版,只获取最后已知位置) * @return 操作结果 diff --git a/local_plugins/open_ai_service/ios/Classes/SystemFunctionHandler.swift b/local_plugins/open_ai_service/ios/Classes/SystemFunctionHandler.swift index 385a28459..81553131d 100644 --- a/local_plugins/open_ai_service/ios/Classes/SystemFunctionHandler.swift +++ b/local_plugins/open_ai_service/ios/Classes/SystemFunctionHandler.swift @@ -570,20 +570,99 @@ private class GetCurrentTimeHandler: FunctionHandler { let result = getCurrentTime(format: format) return "{\"result\": \"\(result.message)\", \"time\": \"\(result.message)\"}" } - + private func getCurrentTime(format: String?) -> Result { - let formatter = DateFormatter() - - if let format = format, !format.isEmpty { - formatter.dateFormat = format - } else { - formatter.dateStyle = .full - formatter.timeStyle = .full - formatter.locale = Locale(identifier: "zh_CN") + let now = Date() + let lang = Locale.current.languageCode ?? "en" + + let timeStr: String + switch format?.lowercased() { + case "short": + timeStr = formatShortTime(now, lang: lang) + case "date": + timeStr = formatDate(now, lang: lang) + case "time": + timeStr = formatTime(now, lang: lang) + case "full": + timeStr = formatFull(now, lang: lang) + case "iso": + let f = DateFormatter() + f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + timeStr = f.string(from: now) + default: + timeStr = formatDefault(now, lang: lang) } - - let currentTime = formatter.string(from: Date()) - return Result(success: true, message: currentTime) + + let resultTime: String + switch format?.lowercased() { + case "short", "time", "iso": + resultTime = timeStr + case "full": + resultTime = timeStr + default: + let weekFormatter = DateFormatter() + weekFormatter.dateFormat = "EEEE" + weekFormatter.locale = Locale.current + resultTime = "\(timeStr) \(weekFormatter.string(from: now))" + } + + return Result(success: true, message: resultTime) + } + + // TTS友好的时间格式,避免AI将":"分隔的时间误读为数字 + private func formatShortTime(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "H时mm分"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "H時mm分"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "H시 mm분"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "h:mm a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatDate(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatTime(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "h:mm:ss a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatFull(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日 EEEE H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日 EEEE H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일 EEEE H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy, EEEE, h:mm:ss a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatDefault(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日 H时mm分"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日 H時mm分"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일 H시 mm분"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy, h:mm a"; f.locale = Locale.current + } + return f.string(from: date) } } diff --git a/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/SystemFunctionHandler.swift b/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/SystemFunctionHandler.swift index 700fe88b2..a57014c9e 100644 --- a/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/SystemFunctionHandler.swift +++ b/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/SystemFunctionHandler.swift @@ -589,20 +589,99 @@ private class GetCurrentTimeHandler: FunctionHandler { let result = getCurrentTime(format: format) return "{\"result\": \"\(result.message)\", \"time\": \"\(result.message)\"}" } - + private func getCurrentTime(format: String?) -> Result { - let formatter = DateFormatter() - - if let format = format, !format.isEmpty { - formatter.dateFormat = format - } else { - formatter.dateStyle = .full - formatter.timeStyle = .full - formatter.locale = Locale(identifier: "zh_CN") + let now = Date() + let lang = Locale.current.languageCode ?? "en" + + let timeStr: String + switch format?.lowercased() { + case "short": + timeStr = formatShortTime(now, lang: lang) + case "date": + timeStr = formatDate(now, lang: lang) + case "time": + timeStr = formatTime(now, lang: lang) + case "full": + timeStr = formatFull(now, lang: lang) + case "iso": + let f = DateFormatter() + f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + timeStr = f.string(from: now) + default: + timeStr = formatDefault(now, lang: lang) } - - let currentTime = formatter.string(from: Date()) - return Result(success: true, message: currentTime) + + let resultTime: String + switch format?.lowercased() { + case "short", "time", "iso": + resultTime = timeStr + case "full": + resultTime = timeStr + default: + let weekFormatter = DateFormatter() + weekFormatter.dateFormat = "EEEE" + weekFormatter.locale = Locale.current + resultTime = "\(timeStr) \(weekFormatter.string(from: now))" + } + + return Result(success: true, message: resultTime) + } + + // TTS友好的时间格式,避免AI将":"分隔的时间误读为数字 + private func formatShortTime(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "H时mm分"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "H時mm分"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "H시 mm분"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "h:mm a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatDate(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatTime(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "h:mm:ss a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatFull(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日 EEEE H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日 EEEE H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일 EEEE H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy, EEEE, h:mm:ss a"; f.locale = Locale.current + } + return f.string(from: date) + } + + private func formatDefault(_ date: Date, lang: String) -> String { + let f = DateFormatter() + switch lang { + case "zh": f.dateFormat = "yyyy年M月d日 H时mm分"; f.locale = Locale(identifier: "zh_CN") + case "ja": f.dateFormat = "yyyy年M月d日 H時mm分"; f.locale = Locale(identifier: "ja_JP") + case "ko": f.dateFormat = "yyyy년 M월 d일 H시 mm분"; f.locale = Locale(identifier: "ko_KR") + default: f.dateFormat = "MMMM d, yyyy, h:mm a"; f.locale = Locale.current + } + return f.string(from: date) } }