Browse Source

上传网络错误优化

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
3d96af9e83
  1. 30
      lib/modules/agent/controllers/agent_controller.dart
  2. 29
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  3. 16
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  4. 47
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  5. 24
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift

30
lib/modules/agent/controllers/agent_controller.dart

@ -707,23 +707,25 @@ class AgentController extends GetxController with WidgetsBindingObserver {
break; break;
case AgentServiceEventType.error: case AgentServiceEventType.error:
//验证错误码 final sessionid = event.data['sessionid'] ?? '';
final message = event.data['message'] ?? '';
// //验证错误码
if (event.data['code'] == 1000) { if (event.data['code'] == 1000) {
isListening.value = false; isListening.value = false;
isSpeaking.value = false; isSpeaking.value = false;
isProcessing.value = false; isProcessing.value = false;
isImageProcessing.value = false; isImageProcessing.value = false;
if (sessionid != "") {
final index = messages.lastIndexWhere(
(msg) => msg.sessionid == sessionid && !msg.isUser);
if (index >= 0) {
messages.removeAt(index);
messages.refresh();
}
}
Get.snackbar('error'.tr, '${event.data['message']}');
} }
// 移除临时的识别消息
final index =
messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser);
if (index >= 0) {
messages.removeAt(index);
// messages.refresh();
}
// Get.snackbar('error'.tr, '${event.data['message']}');
Logger.e(TAG, '代理服务错误: ${event.data['message']}'); Logger.e(TAG, '代理服务错误: ${event.data['message']}');
break; break;
@ -2073,7 +2075,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
color: Colors.grey.withOpacity(0.1), color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: const Icon(Icons.navigation, color: Colors.orange), child: const Icon(Icons.map, color: Colors.orange),
), ),
title: const Text('苹果地图'), title: const Text('苹果地图'),
subtitle: const Text('使用苹果地图导航'), subtitle: const Text('使用苹果地图导航'),
@ -2491,6 +2493,8 @@ class AgentController extends GetxController with WidgetsBindingObserver {
// 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变
// Get.snackbar('开始播放', song.name); // Get.snackbar('开始播放', song.name);
_qqmusicManager.syncCurrentPlayInfo(); _qqmusicManager.syncCurrentPlayInfo();
} else {
Get.snackbar('播放失败', result['error'] ?? '无法播放歌曲');
} }
return; return;
} }
@ -2511,6 +2515,8 @@ class AgentController extends GetxController with WidgetsBindingObserver {
// 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变
// Get.snackbar('开始播放', song.name); // Get.snackbar('开始播放', song.name);
_qqmusicManager.syncCurrentPlayInfo(); _qqmusicManager.syncCurrentPlayInfo();
} else {
Get.snackbar('播放失败', result['error'] ?? '无法播放歌曲');
} }
} }
} }

29
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -39,6 +39,7 @@ import java.util.Date
import android.app.PendingIntent import android.app.PendingIntent
import android.app.NotificationManager import android.app.NotificationManager
import android.app.NotificationChannel import android.app.NotificationChannel
import android.os.Message
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
@ -702,6 +703,7 @@ object AgentService : CoroutineScope {
_isRecognitionActive.set(false) _isRecognitionActive.set(false)
stopIdleCheck() stopIdleCheck()
BleService.closeCodec() BleService.closeCodec()
audioPlayer?.stopAudio()
Log.d(TAG, "语音识别错误: $error") Log.d(TAG, "语音识别错误: $error")
sendEvent( sendEvent(
"error", mapOf( "error", mapOf(
@ -1051,20 +1053,23 @@ object AgentService : CoroutineScope {
} }
} }
override fun onError(sessionid: String, e: Exception) { override fun onError(sessionid: String, code:Int,message: String) {
try { try {
Log.e(TAG, "AI处理出错", e) if (sessionid == currsessionId) {
sendEvent( audioPlayer?.stopAudio()
"error", mapOf( Log.e(TAG, "AI处理出错 $message")
"sessionid" to sessionid, // 优先使用 ChatApiException.code;否则根据底层异常类型推断错误码
"code" to "AI_ERROR", sendEvent(
"message" to e.message.toString() "error", mapOf(
"sessionid" to sessionid,
"code" to code,
"message" to message
)
) )
) // 标记AI流式输出已完成
_isAiStreaming.set(false)
// 标记AI流式输出已完成 currentAiJob = null
_isAiStreaming.set(false) }
currentAiJob = null
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "liwei--------------- AI Call onError 异常", e) Log.e(TAG, "liwei--------------- AI Call onError 异常", e)
} }

16
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift

@ -1708,26 +1708,27 @@ class ChatApiStreamCallback: StreamCallback {
} }
func onError(_ sessionid:String,_ error: Error) { func onError(_ sessionid:String,_ code: Int,_ message:String) {
do { do {
guard let agentService = try agentService else { return } guard let agentService = try agentService else { return }
if sessionid == agentService.currsessionId { if sessionid == agentService.currsessionId {
// 停止等待音效 // 停止等待音效
agentService.audioPlayer?.stopAwaitSound() agentService.audioPlayer?.stopAwaitSound()
}
os_log("ChatAPI错误: %{public}@", log: agentService.logger, type: .error, error.localizedDescription) // os_log("ChatAPI错误: code:%{public}@ message:%{public}@", log: agentService.logger, type: .error, code,message)
if (sessionid != agentService.currsessionId) { if (sessionid != agentService.currsessionId) {
return return
} }
try agentService.sendEvent(name: "error", data: [ try agentService.sendEvent(name: "error", data: [
"sessionid":sessionid, "sessionid":sessionid,
"code": "AI_ERROR", "code": code,
"message": error.localizedDescription "message": message
]) ])
//
agentService.isAiStreaming = false agentService.isAiStreaming = false
}
os_log("因错误设置AI流式状态为false", log: agentService.logger, type: .info) os_log("因错误设置AI流式状态为false", log: agentService.logger, type: .info)
}catch{ }catch{
print("liwei--------------- AI Call onError 异常: \(error)") print("liwei--------------- AI Call onError 异常: \(error)")
@ -2179,7 +2180,8 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
} }
func onError(sessionid:String ,_ errorCode: Int, _ error: String) { func onError(sessionid:String ,_ errorCode: Int, _ error: String) {
let data: [String: Any] = ["sessionid":sessionid,"message": error.isEmpty ? "未知错误" : error] let data: [String: Any] = ["sessionid":sessionid,"code":errorCode, "message": error.isEmpty ? "未知错误" : error]
audioPlayer?.stopAwaitSound()
sendEvent(name: "error", data: data) sendEvent(name: "error", data: data)
isRecognizing = false isRecognizing = false
// 新增:出错时复位"启动中/待停止"状态 // 新增:出错时复位"启动中/待停止"状态

47
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt

@ -29,8 +29,7 @@ import java.util.Collections
/** /**
* ChatAPI服务异常 * ChatAPI服务异常
*/ */
class ChatApiException(message: String) : Exception("ChatApiException: $message") class ChatApiException(message: String, val code: String? = null, cause: Throwable? = null) : Exception("ChatApiException: $message", cause)
/** /**
* 流式回调接口 * 流式回调接口
* 与 iOS 版本 StreamCallback 协议保持完全一致 * 与 iOS 版本 StreamCallback 协议保持完全一致
@ -53,7 +52,7 @@ interface StreamCallback {
/** /**
* 出现错误 * 出现错误
*/ */
fun onError(sessionid: String,error: Exception) fun onError(sessionid: String,code:Int,message:String)
/** /**
* 函数调用 - 兼容JSONObject格式 * 函数调用 - 兼容JSONObject格式
@ -354,13 +353,20 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
if (!isInitialized || apiKey.isEmpty() || openAI == null) { if (!isInitialized || apiKey.isEmpty() || openAI == null) {
Log.e("ChatApiService", "ChatAPI服务未初始化,无法发送消息") Log.e("ChatApiService", "ChatAPI服务未初始化,无法发送消息")
try { try {
getSessionCallback(sessionid)?.onError(sessionid,ChatApiException("ChatAPI服务未初始化")) getSessionCallback(sessionid)?.onError(sessionid,1002,"ChatAPI服务未初始化")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onError回调异常: ${e.message}", e) Log.e(TAG, "onError回调异常: ${e.message}", e)
} }
return return
} }
// 在开始流式请求前检查网络状态
if (!isNetworkAvailable()) {
Log.w("ChatApiService", "[Session: $sessionid] 网络不可用,直接返回网络错误")
getSessionCallback(sessionid)?.onError(sessionid,1000,"网络不可用")
return
}
// 重置状态 // 重置状态
currentMessages = messages currentMessages = messages
toolCalls.clear() toolCalls.clear()
@ -418,7 +424,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
Log.e("ChatApiService", "创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e) Log.e("ChatApiService", "创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e)
if (sessionid == currSessionId) { if (sessionid == currSessionId) {
try { try {
getSessionCallback(sessionid)?.onError(sessionid,ChatApiException("流式请求失败: ${e.message}")) getSessionCallback(sessionid)?.onError(sessionid,1000,"流式请求失败: ${e.message}")
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "onError回调异常: ${ex.message}", ex) Log.e(TAG, "onError回调异常: ${ex.message}", ex)
} }
@ -477,14 +483,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
} }
} }
} }
//else{
// delta.content?.let { content ->
// Log.d("ChatApiService", "liwei------------ [Session: $sessionid] 中间过程不输出 $content")
// }
// }
// Log.d(TAG, "liwei-------------------------开始AI 对话 7-4") // Log.d(TAG, "liwei-------------------------开始AI 对话 7-4")
// 收集工具调用信息 // 收集工具调用信息
delta.toolCalls?.forEach { toolCall -> delta.toolCalls?.forEach { toolCall ->
@ -559,7 +557,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
} catch (e: Exception) { } catch (e: Exception) {
if (sessionid == currSessionId) { if (sessionid == currSessionId) {
try { try {
getSessionCallback(sessionid)?.onError(sessionid,ChatApiException("流式请求失败: ${e.message}")) Log.d(TAG, "AI聊天异常 Session $sessionid 错误类型:${e::class.simpleName} 错误完整类型:${e::class.qualifiedName}")
getSessionCallback(sessionid)?.onError(sessionid,1000,"流式请求失败: ${e.message}")
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "onError回调异常: ${ex.message}", ex) Log.e(TAG, "onError回调异常: ${ex.message}", ex)
} }
@ -632,13 +631,13 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
} }
// 调用MCP工具 // 调用MCP工具
val toolResult = try { val toolResult = try {
withTimeout(60000) { // 60秒超时 withTimeout(10000) { // 100秒超时
// 再次检查会话状态 // 再次检查会话状态
if (sessionid != currSessionId) { if (sessionid != currSessionId) {
throw CancellationException("Session cancelled") throw CancellationException("Session cancelled")
} }
_mcpClient?.callTool(functionName, arguments) _mcpClient?.callTool(functionName, arguments)
} }
} catch (e: TimeoutCancellationException) { } catch (e: TimeoutCancellationException) {
Log.w("ChatApiService", "[Session: $sessionid] MCP工具调用超时: $functionName") Log.w("ChatApiService", "[Session: $sessionid] MCP工具调用超时: $functionName")
mapOf( mapOf(
@ -708,7 +707,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
convertMapToJsonObject(result) convertMapToJsonObject(result)
) )
}else{ }else{
getSessionCallback(sessionid)?.onError(sessionid, ChatApiException(errorText)) Log.d(TAG, "[Session: $sessionid] 工具调用失败 $errorText")
//getSessionCallback(sessionid)?.onError(sessionid, ChatApiException(errorText))
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onFunctionCallResult回调异常: ${e.message}", e) Log.e(TAG, "onFunctionCallResult回调异常: ${e.message}", e)
@ -798,6 +798,19 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
sendMessageStream(sessionid,fullMessages) sendMessageStream(sessionid,fullMessages)
} }
// 添加网络检查方法
private fun isNetworkAvailable(): Boolean {
return try {
val connectivityManager = context?.getSystemService(android.content.Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager
val activeNetwork = connectivityManager?.activeNetworkInfo
activeNetwork?.isConnectedOrConnecting == true
} catch (e: Exception) {
Log.w("ChatApiService", "检查网络状态失败: ${e.message}")
true // 如果检查失败,假设网络可用,让后续的网络请求来处理
}
}
/** /**
* 取消当前流式请求 * 取消当前流式请求
*/ */

24
local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift

@ -20,7 +20,7 @@ public protocol StreamCallback {
func onUsage(_ sessionid: String,_ prompt_tokens: Int?,_ completion_tokens: Int?,_ total_tokens: Int?) func onUsage(_ sessionid: String,_ prompt_tokens: Int?,_ completion_tokens: Int?,_ total_tokens: Int?)
func onToken(_ sessionId:String,_ token: String) func onToken(_ sessionId:String,_ token: String)
func onComplete(_ sessionId:String) func onComplete(_ sessionId:String)
func onError(_ sessionId:String,_ error: Error) func onError(_ sessionId:String,_ code:Int,_ message:String)
func onFunctionCall(_ sessionId:String,_ functionCall: [String: Any]) func onFunctionCall(_ sessionId:String,_ functionCall: [String: Any])
func onFunctionCallResult(_ sessionId:String,_ functionCall: [String: Any], _ functionCallResult: [String: Any]) func onFunctionCallResult(_ sessionId:String,_ functionCall: [String: Any], _ functionCallResult: [String: Any])
} }
@ -59,6 +59,7 @@ public class ChatApiService: NSObject {
private var currentMessages: [[String: Any]] = [] private var currentMessages: [[String: Any]] = []
private var toolCalls: [Int: ToolCallInfo] = [:] private var toolCalls: [Int: ToolCallInfo] = [:]
private var currSessionId = "" private var currSessionId = ""
// MARK: - 初始化 // MARK: - 初始化
public override init() { public override init() {
super.init() super.init()
@ -274,8 +275,7 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
currSessionId = sessionId currSessionId = sessionId
guard isInitialized && !apiKey.isEmpty, let openAI = openAI else { guard isInitialized && !apiKey.isEmpty, let openAI = openAI else {
let error = ChatApiException("ChatAPI服务未初始化") getSessionCallback(sessionId)?.onError(sessionId,1002,"服务未初始化")
getSessionCallback(sessionId)?.onError(sessionId, error)
return return
} }
@ -398,9 +398,9 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
} catch { } catch {
if sessionId == self.currSessionId { if sessionId == self.currSessionId {
let errorMessage = "流式请求失败: \(error.localizedDescription)" // let errorMessage = "流式请求失败: \(error.localizedDescription)"
let chatApiError = ChatApiException(errorMessage) // let chatApiError = ChatApiException(errorMessage)
self.getSessionCallback(sessionId)?.onError(sessionId, chatApiError) self.getSessionCallback(sessionId)?.onError(sessionId, 1001,"流式请求失败: \(error.localizedDescription)")
self.clearCurrentSession() self.clearCurrentSession()
} }
} }
@ -528,12 +528,13 @@ private func processToolCalls(_ sessionId:String) async -> Bool {
functionCall, functionCall,
result result
) )
}else{
self.getSessionCallback(sessionId)?.onError(
sessionId,
ChatApiException(errorStr)
)
} }
//else{ mcp 的错误需要交给ai处理 不需要返回到上层
// self.getSessionCallback(sessionId)?.onError(
// sessionId,
// ChatApiException(errorStr)
// )
// }
// 将结果发送回OpenAI继续对话 // 将结果发送回OpenAI继续对话
await self.sendFunctionCallResultInternal( await self.sendFunctionCallResultInternal(
sessionId: sessionId, sessionId: sessionId,
@ -1009,3 +1010,4 @@ private class LocalFunctionHandler: FunctionHandler {
return "LOCAL_FUNCTION:\(functionName)" return "LOCAL_FUNCTION:\(functionName)"
} }
} }

Loading…
Cancel
Save