diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 4fc81d382..99c5f58ed 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -136,6 +136,8 @@
1
+ NSRemindersUsageDescription
+ This app needs access to reminders to set calendar reminders
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
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 128a4852b..fac77eafc 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
@@ -1373,10 +1373,10 @@ class ChatApiStreamCallback: StreamCallback {
"result": functionCallResult,
"meta":metadata,
])
- os_log("收到函数调用结果! broadcast: %{public}@ reply: %{public}@",
+ os_log("收到函数调用结果! broadcast: %{public}@ reply: %{public}@ %{metadata}@",
log: agentService.logger,
type: .info,
- "\(broadcast)", "\(reply)")
+ "\(broadcast)", "\(reply)", "\(metadata)")
}catch{
print("liwei--------------- AI Call onFunctionCallResult 异常: \(error)")
}
diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
index dec6cd009..1431295de 100644
--- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
+++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
@@ -588,6 +588,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
// 通过MCP客户端处理工具调用
val functionName = firstToolCall.name
val argumentsJson = firstToolCall.arguments
+ var isError = false
+ val errorText = ""
val result = if (_mcpClient?.hasToolWithName(functionName) == true) {
// 解析参数,如果参数为空JSON对象,使用空Map
val arguments = if (argumentsJson == "{}") {
@@ -595,7 +597,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
} else {
_mcpClient?.parseJsonArguments(argumentsJson) ?: emptyMap()
}
-
// 调用MCP工具
val toolResult = _mcpClient?.callTool(functionName, arguments)
Log.d("ChatApiService", "[Session: $sessionid] AI调用工具结果: $functionName, 参数: $arguments, 结果: $toolResult")
@@ -603,9 +604,10 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
if (toolResult != null) {
if (toolResult["isError"] == true) {
// 处理错误情况
+ isError = true
val content = toolResult["content"] as? List<*>
val firstContent = content?.firstOrNull() as? Map<*, *>
- val errorText = firstContent?.get("text") as? String ?: "Tool execution failed"
+ errorText = firstContent?.get("text") as? String ?: "Tool execution failed"
mapOf("context" to errorText)
} else if (toolResult.containsKey("context")) {
// 本地函数结果
@@ -628,11 +630,15 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
if (sessionid == currSessionId) {
// 处理结果
try {
- getSessionCallback(sessionid)?.onFunctionCallResult(
- sessionid,
- convertMapToJsonObject(functionCall),
- convertMapToJsonObject(result)
- )
+ if(!isError){
+ getSessionCallback(sessionid)?.onFunctionCallResult(
+ sessionid,
+ convertMapToJsonObject(functionCall),
+ convertMapToJsonObject(result)
+ )
+ }else{
+ getSessionCallback(sessionid)?.onError(sessionid, ChatApiException(errorText))
+ }
} catch (e: Exception) {
Log.e(TAG, "onFunctionCallResult回调异常: ${e.message}", e)
}
diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift
index 8c6a37f0f..b23b6dca5 100644
--- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift
+++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift
@@ -481,7 +481,8 @@ getSessionCallback(sessionId)?.onFunctionCall(sessionId, jsonFunctionCall)
// 通过MCP客户端处理工具调用
var result: [String: Any] = [:]
-
+ var isError = false
+ var errorStr = ""
if let client = self.mcpClient {
// 调用MCP工具
if let toolResult = await client.callTool(name: firstToolCall.name, arguments: args) {
@@ -492,6 +493,7 @@ getSessionCallback(sessionId)?.onFunctionCall(sessionId, jsonFunctionCall)
if let content = toolResult["content"] as? [[String: Any]],
let firstContent = content.first,
let errorText = firstContent["text"] as? String {
+ errorStr = errorText
result = ["context": errorText]
} else {
result = ["context": "Tool execution failed"]
@@ -519,11 +521,19 @@ getSessionCallback(sessionId)?.onFunctionCall(sessionId, jsonFunctionCall)
if sessionId == self.currSessionId {
// 处理结果
- self.getSessionCallback(sessionId)?.onFunctionCallResult(
- sessionId,
- functionCall,
- result
- )
+ if (!isError){
+ self.getSessionCallback(sessionId)?.onFunctionCallResult(
+ sessionId,
+ functionCall,
+ result
+ )
+ }else{
+ self.getSessionCallback(sessionId)?.onError(
+ sessionId,
+ ChatApiException(errorStr)
+ )
+ }
+
if (firstToolCall.name != "set_user_profile_field"){
// 将结果发送回OpenAI继续对话
await self.sendFunctionCallResultInternal(
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 a3f295014..e1b6368e3 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
@@ -84,10 +84,10 @@ public class SystemFunctionHandler: NSObject {
registerGetCurrentLocationFunction(client: client)
// 注册媒体控制函数
- registerMediaPlayFunction(client: client)
- registerMediaPauseFunction(client: client)
- registerMediaPreviousFunction(client: client)
- registerMediaNextFunction(client: client)
+// registerMediaPlayFunction(client: client)
+// registerMediaPauseFunction(client: client)
+// registerMediaPreviousFunction(client: client)
+// registerMediaNextFunction(client: client)
// 注册打开录音机功能
registerOpenRecorderFunction(client: client)
@@ -735,8 +735,20 @@ private class SetReminderHandler: FunctionHandler {
// 请求日历权限
let status = await requestCalendarAccess()
- guard status == .authorized else {
- return Result(success: false, message: "日历权限被拒绝")
+ print("[SetReminder] 权限检查结果: \(status.rawValue) - \(statusDescription(status))")
+
+ switch status {
+ case .authorized:
+ // 权限已授权,继续执行
+ break
+ case .denied:
+ return Result(success: false, message: "日历权限被拒绝,请前往 设置 > 隐私与安全 > 日历 中开启权限")
+ case .restricted:
+ return Result(success: false, message: "日历权限受限制,可能由于家长控制或企业策略限制")
+ case .notDetermined:
+ return Result(success: false, message: "日历权限未确定,请重试")
+ @unknown default:
+ return Result(success: false, message: "日历权限状态未知")
}
// 解析时间
@@ -765,12 +777,48 @@ private class SetReminderHandler: FunctionHandler {
private func requestCalendarAccess() async -> EKAuthorizationStatus {
return await withCheckedContinuation { continuation in
+ // 首先检查当前权限状态
+ let currentStatus = EKEventStore.authorizationStatus(for: .reminder)
+ print("[Calendar] 当前权限状态: \(currentStatus.rawValue) - \(statusDescription(currentStatus))")
+
+ // 如果已经授权,直接返回
+ if currentStatus == .authorized {
+ continuation.resume(returning: currentStatus)
+ return
+ }
+
+ // 如果被拒绝,也直接返回(避免重复请求)
+ if currentStatus == .denied {
+ print("[Calendar] 权限已被拒绝,需要用户手动在设置中开启")
+ continuation.resume(returning: currentStatus)
+ return
+ }
+
+ // 请求权限
eventStore?.requestAccess(to: .reminder) { granted, error in
- let status = EKEventStore.authorizationStatus(for: .reminder)
- continuation.resume(returning: status)
+ let finalStatus = EKEventStore.authorizationStatus(for: .reminder)
+ print("[Calendar] 权限请求完成 - granted: \(granted), error: \(error?.localizedDescription ?? "无")")
+ print("[Calendar] 最终权限状态: \(finalStatus.rawValue) - \(self.statusDescription(finalStatus))")
+ continuation.resume(returning: finalStatus)
}
}
}
+
+ // 添加状态描述辅助方法
+ private func statusDescription(_ status: EKAuthorizationStatus) -> String {
+ switch status {
+ case .notDetermined:
+ return "未确定"
+ case .restricted:
+ return "受限制"
+ case .denied:
+ return "被拒绝"
+ case .authorized:
+ return "已授权"
+ @unknown default:
+ return "未知状态"
+ }
+ }
}
diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift
index faccc1834..a626a1e37 100644
--- a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift
+++ b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift
@@ -23,13 +23,6 @@ public class LocationService: NSObject {
private var authorizationStatus: CLAuthorizationStatus = .notDetermined
private var isLocationServicesEnabled: Bool = false
- // 高精度定位属性
- private var locationAttempts: Int = 0
- private let maxLocationAttempts: Int = 3
- private let requiredAccuracy: CLLocationAccuracy = 10.0 // 要求精度在10米以内
- private var bestLocation: CLLocation?
- private var locationTimer: Timer?
-
// MARK: - 权限回调
private var permissionCallback: ((Result) -> Void)?
@@ -43,11 +36,33 @@ public class LocationService: NSObject {
self?.locationManager = CLLocationManager()
self?.locationManager?.delegate = self
- // 高精度定位设置
- self?.locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation // 最高精度
- self?.locationManager?.distanceFilter = 1 // 降低距离过滤器,更敏感
+ // 正常定位设置
+ self?.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
+ self?.locationManager?.distanceFilter = 10
print("[LocationService] LocationManager 初始化完成,等待权限状态回调")
+
+ // 初始化完成后,主动获取一次位置进行预热
+ self?.preWarmLocation()
+ }
+ }
+
+ /// 预热定位服务
+ private func preWarmLocation() {
+ // 检查权限状态
+ if checkLocationPermission() {
+ print("[LocationService] 开始预热定位")
+ requestLocation { [weak self] result in
+ switch result {
+ case .success(let locationData):
+ self?.cachedLocationData = locationData
+ print("[LocationService] 预热定位成功,已缓存位置数据")
+ case .failure(let error):
+ print("[LocationService] 预热定位失败: \(error.localizedDescription)")
+ }
+ }
+ } else {
+ print("[LocationService] 无定位权限,跳过预热")
}
}
@@ -95,7 +110,7 @@ public class LocationService: NSObject {
private func asyncUpdateLocation() {
// 检查权限
if checkLocationPermission() {
- requestHighAccuracyLocation { [weak self] result in
+ requestLocation { [weak self] result in
switch result {
case .success(let locationData):
self?.cachedLocationData = locationData
@@ -110,7 +125,7 @@ public class LocationService: NSObject {
/// 同步获取位置
private func syncGetLocation(completion: @escaping (Result<[String: Any], Error>) -> Void) {
if checkLocationPermission() {
- requestHighAccuracyLocation { [weak self] result in
+ requestLocation { [weak self] result in
switch result {
case .success(let locationData):
self?.cachedLocationData = locationData
@@ -168,8 +183,8 @@ public class LocationService: NSObject {
}
}
- /// 高精度定位方法
- private func requestHighAccuracyLocation(completion: @escaping (Result) -> Void) {
+ /// 正常定位方法
+ private func requestLocation(completion: @escaping (Result) -> Void) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@@ -190,10 +205,6 @@ public class LocationService: NSObject {
return
}
- // 重置状态
- self.locationAttempts = 0
- self.bestLocation = nil
-
// 设置回调
self.currentLocationCallback = { result in
switch result {
@@ -217,41 +228,22 @@ public class LocationService: NSObject {
}
}
- print("[LocationService] 开始高精度连续定位")
- // 开始连续定位
- locationManager.startUpdatingLocation()
-
- // 设置超时定时器(15秒)
- self.locationTimer = Timer.scheduledTimer(withTimeInterval: 15.0, repeats: false) { _ in
- self.stopLocationUpdates()
- if let bestLocation = self.bestLocation {
- self.processLocation(bestLocation)
- } else {
- let callback = self.currentLocationCallback
- self.currentLocationCallback = nil
- callback?(.failure(NSError(domain: "LocationService", code: -9, userInfo: [NSLocalizedDescriptionKey: "无法获取满足精度要求的位置"])))
- }
- }
+ print("[LocationService] 开始正常定位")
+ // 使用单次定位
+ locationManager.requestLocation()
}
}
- /// 停止位置更新
- private func stopLocationUpdates() {
- locationManager?.stopUpdatingLocation()
- locationTimer?.invalidate()
- locationTimer = nil
- }
-
/// 处理位置数据
private func processLocation(_ location: CLLocation) {
let locationInfo: [String: Any] = [
"user_lng": String(location.coordinate.longitude),
"user_lat": String(location.coordinate.latitude),
"timestamp": Date().timeIntervalSince1970 * 1000,
- "accuracy": location.horizontalAccuracy // 添加精度信息
+ "accuracy": location.horizontalAccuracy
]
- print("[LocationService] 处理最终位置,精度: \(location.horizontalAccuracy)m")
+ print("[LocationService] 处理位置,精度: \(location.horizontalAccuracy)m")
// 更新缓存
cachedLocationData = LocationData(
@@ -288,8 +280,7 @@ extension LocationService: CLLocationManagerDelegate {
}
print("[LocationService] 位置更新: lat=\(location.coordinate.latitude), lng=\(location.coordinate.longitude)")
- print("[LocationService] 水平精度: \(location.horizontalAccuracy)m, 垂直精度: \(location.verticalAccuracy)m")
- print("[LocationService] 位置时间: \(location.timestamp)")
+ print("[LocationService] 水平精度: \(location.horizontalAccuracy)m")
// 过滤无效位置
guard location.horizontalAccuracy >= 0 else {
@@ -304,39 +295,14 @@ extension LocationService: CLLocationManagerDelegate {
return
}
- locationAttempts += 1
-
- // 检查是否满足精度要求
- if location.horizontalAccuracy <= requiredAccuracy {
- print("[LocationService] 获得高精度位置,精度: \(location.horizontalAccuracy)m")
- stopLocationUpdates()
- processLocation(location)
- return
- }
-
- // 保存最佳位置
- if bestLocation == nil || location.horizontalAccuracy < bestLocation!.horizontalAccuracy {
- bestLocation = location
- print("[LocationService] 更新最佳位置,精度: \(location.horizontalAccuracy)m")
- }
-
- // 达到最大尝试次数,使用最佳位置
- if locationAttempts >= maxLocationAttempts {
- print("[LocationService] 达到最大尝试次数,使用最佳位置")
- stopLocationUpdates()
- if let bestLocation = bestLocation {
- processLocation(bestLocation)
- }
- }
+ // 处理位置
+ processLocation(location)
}
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("[LocationService] didFailWithError 被调用")
print("[LocationService] 位置获取失败: \(error.localizedDescription)")
- print("[LocationService] 错误代码: \((error as NSError).code)")
- print("[LocationService] 错误域: \((error as NSError).domain)")
- stopLocationUpdates()
let callback = currentLocationCallback
currentLocationCallback = nil
callback?(.failure(error))