Browse Source

上传网络错误优化

weicu
liwei1dao 1 year ago
parent
commit
ee544b37f5
  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;
case AgentServiceEventType.error:
//验证错误码
final sessionid = event.data['sessionid'] ?? '';
final message = event.data['message'] ?? '';
// //验证错误码
if (event.data['code'] == 1000) {
isListening.value = false;
isSpeaking.value = false;
isProcessing.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']}');
break;
@ -2073,7 +2075,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.navigation, color: Colors.orange),
child: const Icon(Icons.map, color: Colors.orange),
),
title: const Text('苹果地图'),
subtitle: const Text('使用苹果地图导航'),
@ -2491,6 +2493,8 @@ class AgentController extends GetxController with WidgetsBindingObserver {
// 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变
// Get.snackbar('开始播放', song.name);
_qqmusicManager.syncCurrentPlayInfo();
} else {
Get.snackbar('播放失败', result['error'] ?? '无法播放歌曲');
}
return;
}
@ -2511,6 +2515,8 @@ class AgentController extends GetxController with WidgetsBindingObserver {
// 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变
// Get.snackbar('开始播放', song.name);
_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.NotificationManager
import android.app.NotificationChannel
import android.os.Message
import androidx.core.app.NotificationCompat
@ -702,6 +703,7 @@ object AgentService : CoroutineScope {
_isRecognitionActive.set(false)
stopIdleCheck()
BleService.closeCodec()
audioPlayer?.stopAudio()
Log.d(TAG, "语音识别错误: $error")
sendEvent(
"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 {
Log.e(TAG, "AI处理出错", e)
sendEvent(
"error", mapOf(
"sessionid" to sessionid,
"code" to "AI_ERROR",
"message" to e.message.toString()
if (sessionid == currsessionId) {
audioPlayer?.stopAudio()
Log.e(TAG, "AI处理出错 $message")
// 优先使用 ChatApiException.code;否则根据底层异常类型推断错误码
sendEvent(
"error", mapOf(
"sessionid" to sessionid,
"code" to code,
"message" to message
)
)
)
// 标记AI流式输出已完成
_isAiStreaming.set(false)
currentAiJob = null
// 标记AI流式输出已完成
_isAiStreaming.set(false)
currentAiJob = null
}
} catch (e: Exception) {
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 {
guard let agentService = try agentService else { return }
if sessionid == agentService.currsessionId {
// 停止等待音效
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) {
return
}
try agentService.sendEvent(name: "error", data: [
"sessionid":sessionid,
"code": "AI_ERROR",
"message": error.localizedDescription
"code": code,
"message": message
])
//
agentService.isAiStreaming = false
}
os_log("因错误设置AI流式状态为false", log: agentService.logger, type: .info)
}catch{
print("liwei--------------- AI Call onError 异常: \(error)")
@ -2179,7 +2180,8 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
}
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)
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服务异常
*/
class ChatApiException(message: String) : Exception("ChatApiException: $message")
class ChatApiException(message: String, val code: String? = null, cause: Throwable? = null) : Exception("ChatApiException: $message", cause)
/**
* 流式回调接口
* 与 iOS 版本 StreamCallback 协议保持完全一致
@ -53,7 +52,7 @@ interface StreamCallback {
/**
* 出现错误
*/
fun onError(sessionid: String,error: Exception)
fun onError(sessionid: String,code:Int,message:String)
/**
* 函数调用 - 兼容JSONObject格式
@ -354,13 +353,20 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
if (!isInitialized || apiKey.isEmpty() || openAI == null) {
Log.e("ChatApiService", "ChatAPI服务未初始化,无法发送消息")
try {
getSessionCallback(sessionid)?.onError(sessionid,ChatApiException("ChatAPI服务未初始化"))
getSessionCallback(sessionid)?.onError(sessionid,1002,"ChatAPI服务未初始化")
} catch (e: Exception) {
Log.e(TAG, "onError回调异常: ${e.message}", e)
}
return
}
// 在开始流式请求前检查网络状态
if (!isNetworkAvailable()) {
Log.w("ChatApiService", "[Session: $sessionid] 网络不可用,直接返回网络错误")
getSessionCallback(sessionid)?.onError(sessionid,1000,"网络不可用")
return
}
// 重置状态
currentMessages = messages
toolCalls.clear()
@ -418,7 +424,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
Log.e("ChatApiService", "创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e)
if (sessionid == currSessionId) {
try {
getSessionCallback(sessionid)?.onError(sessionid,ChatApiException("流式请求失败: ${e.message}"))
getSessionCallback(sessionid)?.onError(sessionid,1000,"流式请求失败: ${e.message}")
} catch (ex: Exception) {
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")
// 收集工具调用信息
delta.toolCalls?.forEach { toolCall ->
@ -559,7 +557,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
} catch (e: Exception) {
if (sessionid == currSessionId) {
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) {
Log.e(TAG, "onError回调异常: ${ex.message}", ex)
}
@ -632,13 +631,13 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
}
// 调用MCP工具
val toolResult = try {
withTimeout(60000) { // 60秒超时
withTimeout(10000) { // 100秒超时
// 再次检查会话状态
if (sessionid != currSessionId) {
throw CancellationException("Session cancelled")
}
_mcpClient?.callTool(functionName, arguments)
}
}
} catch (e: TimeoutCancellationException) {
Log.w("ChatApiService", "[Session: $sessionid] MCP工具调用超时: $functionName")
mapOf(
@ -708,7 +707,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
convertMapToJsonObject(result)
)
}else{
getSessionCallback(sessionid)?.onError(sessionid, ChatApiException(errorText))
Log.d(TAG, "[Session: $sessionid] 工具调用失败 $errorText")
//getSessionCallback(sessionid)?.onError(sessionid, ChatApiException(errorText))
}
} catch (e: Exception) {
Log.e(TAG, "onFunctionCallResult回调异常: ${e.message}", e)
@ -798,6 +798,19 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
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 onToken(_ sessionId:String,_ token: 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 onFunctionCallResult(_ sessionId:String,_ functionCall: [String: Any], _ functionCallResult: [String: Any])
}
@ -59,6 +59,7 @@ public class ChatApiService: NSObject {
private var currentMessages: [[String: Any]] = []
private var toolCalls: [Int: ToolCallInfo] = [:]
private var currSessionId = ""
// MARK: - 初始化
public override init() {
super.init()
@ -274,8 +275,7 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
currSessionId = sessionId
guard isInitialized && !apiKey.isEmpty, let openAI = openAI else {
let error = ChatApiException("ChatAPI服务未初始化")
getSessionCallback(sessionId)?.onError(sessionId, error)
getSessionCallback(sessionId)?.onError(sessionId,1002,"服务未初始化")
return
}
@ -398,9 +398,9 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
} catch {
if sessionId == self.currSessionId {
let errorMessage = "流式请求失败: \(error.localizedDescription)"
let chatApiError = ChatApiException(errorMessage)
self.getSessionCallback(sessionId)?.onError(sessionId, chatApiError)
// let errorMessage = "流式请求失败: \(error.localizedDescription)"
// let chatApiError = ChatApiException(errorMessage)
self.getSessionCallback(sessionId)?.onError(sessionId, 1001,"流式请求失败: \(error.localizedDescription)")
self.clearCurrentSession()
}
}
@ -528,12 +528,13 @@ private func processToolCalls(_ sessionId:String) async -> Bool {
functionCall,
result
)
}else{
self.getSessionCallback(sessionId)?.onError(
sessionId,
ChatApiException(errorStr)
)
}
//else{ mcp 的错误需要交给ai处理 不需要返回到上层
// self.getSessionCallback(sessionId)?.onError(
// sessionId,
// ChatApiException(errorStr)
// )
// }
// 将结果发送回OpenAI继续对话
await self.sendFunctionCallResultInternal(
sessionId: sessionId,
@ -1009,3 +1010,4 @@ private class LocalFunctionHandler: FunctionHandler {
return "LOCAL_FUNCTION:\(functionName)"
}
}

Loading…
Cancel
Save