Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
tanlongsheng 1 year ago
parent
commit
037d95c4b0
  1. 4
      ios/Runner/Info.plist
  2. 13
      lib/core/translations/language/en_us.dart
  3. 9
      lib/core/translations/language/zh_cn.dart
  4. 11
      lib/data/models/appconfig.dart
  5. 22
      lib/data/models/user_Info.dart
  6. 170
      lib/modules/pairing/controllers/pairing_controller.dart
  7. 97
      lib/modules/settings/views/settings_view.dart
  8. 374
      lib/modules/splash/controllers/splash_controller.dart
  9. 2
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  10. 4
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  11. 4
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AudioSessionHub.swift
  12. 227
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift
  13. 77
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift
  14. 18
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift

4
ios/Runner/Info.plist

@ -121,10 +121,6 @@
<string>audio</string>
<string>location</string>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>telephony</string>
</array>
<key>UIDeviceFamily</key>
<array>
<string>1</string>

13
lib/core/translations/language/en_us.dart

@ -813,6 +813,19 @@ const Map<String, String> enUS = {
'deviceAuthorizationFailedCheckCode':
'Device authorization failed, please check authorization code',
///iOS蓝牙引导窗
'bluetoothGuideTitle': 'Manual Bluetooth Activation Required', // 需要手动开启蓝牙
'bluetoothGuideSteps': 'Please follow these steps:', // 请按照以下步骤操作:
'bluetoothGuideStep1':
'Enable Bluetooth: Settings → Bluetooth → Turn On', // 开启蓝牙:设置 → 蓝牙 → 开启
'bluetoothGuideStep2':
'Connect Device: Find your device in "My Devices" and tap to connect', // 连接设备:在"我的设备"中找到您的设备并点击连接
'bluetoothGuideStep3':
'Return to app to continue pairing process', // 返回应用继续配对流程
'bluetoothGuideTip':
'Tip: You can also long press the Bluetooth icon in Control Center to quickly access Bluetooth settings', // 提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置
'iKnow': 'I Know', // 我知道了
// 用户反馈页
"feedback": "Feedback",
'feedbackInfo': 'Provide feedback or suggestions',

9
lib/core/translations/language/zh_cn.dart

@ -746,6 +746,15 @@ const Map<String, String> zhCN = {
'bleConnectionFailed': 'BLE连接失败',
'deviceAuthorizationFailedCheckCode': '设备授权失败,请检查授权码',
///iOS蓝牙引导窗
'bluetoothGuideTitle': '需要手动开启蓝牙', // 需要手动开启蓝牙
'bluetoothGuideSteps': '请按照以下步骤操作:', // 请按照以下步骤操作:
'bluetoothGuideStep1': '开启蓝牙:设置 → 蓝牙 → 开启', // 开启蓝牙:设置 → 蓝牙 → 开启
'bluetoothGuideStep2': '连接设备:在"我的设备"中找到您的设备并点击连接', // 连接设备:在"我的设备"中找到您的设备并点击连接
'bluetoothGuideStep3': '返回应用继续配对流程', // 返回应用继续配对流程
'bluetoothGuideTip': '提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置', // 提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置
'iKnow': '我知道了', // 我知道了
//用户反馈页
"feedback": "用户反馈",
'feedbackInfo': '提供反馈或建议',

11
lib/data/models/appconfig.dart

@ -4,9 +4,20 @@ import '../../../data/models/appconfig_model.dart';
class AppConfig {
static late UserGetAppConfigResp config;
// 新增一个私有的静态布尔值来跟踪初始化状态
static bool _isInitialized = false;
/// 检查AppConfig单例是否已经被初始化
/// 外部通过调用此方法来安全地检查状态
static bool isInitialized() {
return _isInitialized;
}
// 可选:提供初始化方法
static void initialize(UserGetAppConfigResp resp) {
config = resp;
_isInitialized = true; // <-- 新增此行
}
//环境变量

22
lib/data/models/user_Info.dart

@ -1,9 +1,15 @@
import 'dart:ffi';
import 'package:get_storage/get_storage.dart';
import '../../../core/utils/logger.dart';
class User {
// 单例实例
static User? _instance;
/// 安全地检查用户是否已经登录(即单例是否已初始化)
static bool isLoggedIn() {
return _instance != null;
}
// 单例访问器(增加空安全保护)
static User get instance {
if (_instance == null) {
@ -12,6 +18,18 @@ class User {
return _instance!;
}
// --- 新增 ---
/// 清理所有与用户会话相关的数据
static Future<void> clearUserSession() async {
final GetStorage storage = GetStorage();
// 1. 清理本地存储
await storage.remove('logintoken');
await storage.remove('user_info');
// 2. 清理内存中的单例
_instance = null;
Logger.i("User", "用户会话数据已安全清理。");
}
// 最终属性(不可变)
String name;
String uid;
@ -72,7 +90,7 @@ class User {
// 修复1:安全获取嵌套的 user 字段
final userData = json['user'] as Map<String, dynamic>?; // 允许空值
final devicesData = json['devices'] as List<dynamic>?; // 允许空值
print("数据获取fronTokenJson${userData} ${devicesData}");
print("数据获取fronTokenJson$userData $devicesData");
// 更新单例实例
return _instance = User._(
token: '',

170
lib/modules/pairing/controllers/pairing_controller.dart

@ -1,12 +1,10 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:app_settings/app_settings.dart';
import 'package:voitrans/data/models/appconfig_model.dart';
import '/data/models/appconfig.dart';
import '/data/models/user_Info.dart';
import '/data/services/network/api.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'dart:async';
import 'package:permission_handler/permission_handler.dart';
@ -16,7 +14,6 @@ import 'dart:io';
import '../../../data/services/bluetooth_manager.dart';
import '../../../data/services/ble_manager.dart';
import '../../../core/utils/permission_util.dart';
import 'package:url_launcher/url_launcher.dart';
/// 配对状态枚举 - 简化版本
enum PairingState {
@ -47,7 +44,7 @@ enum ErrorType {
}
/// 配对控制器 - 基于简化状态机的实现
class PairingController extends GetxController {
class PairingController extends GetxController with WidgetsBindingObserver {
static const String _tag = 'PairingCtrl';
static PairingController get to => Get.find();
@ -103,6 +100,11 @@ class PairingController extends GetxController {
super.onInit();
Logger.i(_tag, '配对控制器初始化');
// 注册应用生命周期监听器
Logger.i(_tag, '正在注册应用生命周期监听器');
WidgetsBinding.instance.addObserver(this);
Logger.i(_tag, '应用生命周期监听器注册完成');
// 确保服务已初始化
_bluetoothManager = Get.find<BluetoothManager>();
_bleManager = Get.find<BleManager>();
@ -126,6 +128,10 @@ class PairingController extends GetxController {
@override
void onClose() {
Logger.i(_tag, '配对控制器销毁');
// 移除应用生命周期监听器
WidgetsBinding.instance.removeObserver(this);
// 清理所有Worker
for (var worker in _workers) {
worker.dispose();
@ -138,6 +144,109 @@ class PairingController extends GetxController {
super.onClose();
}
/// 应用生命周期状态变化回调
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
// 使用多种日志级别确保输出
Logger.i(_tag, '=== 应用生命周期状态变化: $state ===');
print('PairingCtrl: 应用生命周期状态变化: $state'); // 直接使用print确保输出
switch (state) {
case AppLifecycleState.resumed:
// 应用从后台切回前台
Logger.i(_tag, '=== 应用从后台切回前台 ===');
print('PairingCtrl: 应用从后台切回前台');
_onAppResumed();
break;
case AppLifecycleState.paused:
// 应用切到后台
Logger.i(_tag, '应用切到后台');
print('PairingCtrl: 应用切到后台');
_onAppPaused();
break;
case AppLifecycleState.inactive:
// 应用处于非活跃状态(如来电话时)
Logger.i(_tag, '应用处于非活跃状态');
print('PairingCtrl: 应用处于非活跃状态');
break;
case AppLifecycleState.detached:
// 应用即将被销毁
Logger.i(_tag, '应用即将被销毁');
print('PairingCtrl: 应用即将被销毁');
break;
case AppLifecycleState.hidden:
// 应用被隐藏(iOS 13+)
Logger.i(_tag, '应用被隐藏');
print('PairingCtrl: 应用被隐藏');
break;
}
}
/// 应用从后台切回前台时的处理逻辑
void _onAppResumed() {
Logger.i(_tag, '处理应用从后台切回前台事件');
// 刷新蓝牙连接状态
_bluetoothManager.refreshDeviceConnection();
// 根据当前配对状态执行相应操作
switch (_currentState.value) {
case PairingState.waitingClassic:
// 如果在等待经典蓝牙连接,重新检查连接状态
Logger.i(_tag, '重新检查经典蓝牙连接状态');
_checkCurrentStatus();
break;
case PairingState.waitingBle:
// 如果在等待BLE连接,重新检查BLE状态
Logger.i(_tag, '重新检查BLE连接状态');
_checkBleConnectionAfterResume();
break;
case PairingState.error:
// 如果在错误状态,根据错误类型决定是否自动重试
if (_errorType.value == ErrorType.bluetoothOff) {
Logger.i(_tag, '检查蓝牙是否已重新开启');
_checkCurrentStatus();
}
break;
default:
// 其他状态暂不处理
break;
}
}
/// 应用切到后台时的处理逻辑
void _onAppPaused() {
Logger.i(_tag, '处理应用切到后台事件');
// 可以在这里暂停一些不必要的操作,如蓝牙扫描等
if (_currentState.value == PairingState.associating) {
Logger.i(_tag, '应用切到后台,暂停BLE扫描');
_bleManager.stopScan();
}
}
/// 应用恢复后检查BLE连接状态
Future<void> _checkBleConnectionAfterResume() async {
try {
final currentBleState = await _bleManager.getConnectionState();
Logger.i(_tag, '应用恢复后BLE连接状态: $currentBleState');
if (currentBleState == 2) {
// BLE已连接,直接完成配对
Logger.i(_tag, '检测到BLE已连接,完成配对');
_bleTimeoutTimer?.cancel();
_transition(PairingState.completed);
} else {
// BLE未连接,重新启动连接流程
Logger.i(_tag, '重新启动BLE连接流程');
_startBleConnection();
}
} catch (e) {
Logger.e(_tag, '检查BLE连接状态失败: $e');
}
}
// ==================== 监听器设置 ====================
/// 集中设置所有监听器
@ -1028,52 +1137,65 @@ class PairingController extends GetxController {
void _showBluetoothGuideDialog() {
Get.dialog(
AlertDialog(
title: Text('需要手动开启蓝牙'),
title: Text('bluetoothGuideTitle'.tr),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('请按照以下步骤操作:'),
SizedBox(height: 16),
Text('bluetoothGuideSteps'.tr),
SizedBox(height: 16.h),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('1. ', style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(child: Text('开启蓝牙:设置 → 蓝牙 → 开启')),
Text('1. ',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 14.sp)),
Expanded(
child: Text('bluetoothGuideStep1'.tr,
style: TextStyle(fontSize: 14.sp))),
],
),
SizedBox(height: 8),
SizedBox(height: 8.h),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('2. ', style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(child: Text('连接设备:在"我的设备"中找到您的设备并点击连接')),
Text('2. ',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 14.sp)),
Expanded(
child: Text('bluetoothGuideStep2'.tr,
style: TextStyle(fontSize: 14.sp))),
],
),
SizedBox(height: 8),
SizedBox(height: 8.h),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('3. ', style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(child: Text('返回应用继续配对流程')),
Text('3. ',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 14.sp)),
Expanded(
child: Text('bluetoothGuideStep3'.tr,
style: TextStyle(fontSize: 14.sp))),
],
),
SizedBox(height: 16),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.all(12),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.blue.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.info_outline, color: Colors.blue, size: 20),
SizedBox(width: 8),
Icon(Icons.info_outline, color: Colors.blue, size: 20.sp),
SizedBox(width: 8.w),
Expanded(
child: Text(
'提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置',
style: TextStyle(fontSize: 12, color: Colors.blue[700]),
'bluetoothGuideTip'.tr,
style:
TextStyle(fontSize: 12.sp, color: Colors.blue[700]),
),
),
],
@ -1084,7 +1206,7 @@ class PairingController extends GetxController {
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('我知道了'),
child: Text('iKnow'.tr, style: TextStyle(fontSize: 14.sp)),
),
],
),

97
lib/modules/settings/views/settings_view.dart

@ -407,8 +407,6 @@ class SettingsView extends GetView<SettingsController> {
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 用户反馈
_buildSimpleNavigationSetting(
title: 'feedback'.tr, // 用户反馈
@ -427,29 +425,33 @@ class SettingsView extends GetView<SettingsController> {
},
isDarkMode: isDarkMode,
),
Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
if (Platform.isAndroid) ...[
Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 隐私设置
_buildSimpleNavigationSetting(
title: 'privacySettings'.tr, // 隐私设置
subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限
icon: Icons.shield_outlined,
iconBgColor: isDarkMode
? Colors.purple[900]!.withOpacity(0.3)
: Colors.purple[100]!,
iconColor: isDarkMode
? Colors.purple[300]!
: Colors.purple[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
AppSettings.openAppSettings();
},
isDarkMode: isDarkMode,
)
],
// 隐私设置
_buildSimpleNavigationSetting(
title: 'privacySettings'.tr, // 隐私设置
subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限
icon: Icons.shield_outlined,
iconBgColor: isDarkMode
? Colors.purple[900]!.withOpacity(0.3)
: Colors.purple[100]!,
iconColor:
isDarkMode ? Colors.purple[300]! : Colors.purple[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
AppSettings.openAppSettings();
},
isDarkMode: isDarkMode,
),
Divider(
height: 1,
color: isDarkMode
@ -597,29 +599,32 @@ class SettingsView extends GetView<SettingsController> {
onTap: () => Get.toNamed(Routes.OTA_UPGRADE),
isDarkMode: isDarkMode,
),
Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
if (Platform.isAndroid) ...[
Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 添加权限管理设置项
_buildSimpleNavigationSetting(
title: 'permissionManagement'.tr, // 权限管理
subtitle: 'permissionManagementInfo'.tr, // 管理应用权限设置
icon: Icons.security,
iconBgColor: isDarkMode
? Colors.orange[900]!.withOpacity(0.3)
: Colors.orange[100]!,
iconColor:
isDarkMode ? Colors.orange[300]! : Colors.orange[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
Get.toNamed(Routes.permissions);
},
isDarkMode: isDarkMode,
),
// 添加权限管理设置项
_buildSimpleNavigationSetting(
title: 'permissionManagement'.tr, // 权限管理
subtitle: 'permissionManagementInfo'.tr, // 管理应用权限设置
icon: Icons.security,
iconBgColor: isDarkMode
? Colors.orange[900]!.withOpacity(0.3)
: Colors.orange[100]!,
iconColor: isDarkMode
? Colors.orange[300]!
: Colors.orange[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
Get.toNamed(Routes.permissions);
},
isDarkMode: isDarkMode,
),
]
],
),
),

374
lib/modules/splash/controllers/splash_controller.dart

@ -1,15 +1,14 @@
import 'dart:async';
import 'dart:io';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import '../../../data/models/appconfig.dart';
import '../../../data/models/appconfig_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../../../data/models/appconfig.dart';
import '../../../data/models/appconfig_model.dart';
import '../../../data/models/user_Info.dart';
import '../../../routes/app_routes.dart';
import '../../../core/themes/theme_service.dart';
import '../../../data/services/network/api.dart';
import '../../../data/services/version_update_service.dart';
import '../../../modules/common/widgets/version_update_dialog.dart';
@ -24,178 +23,174 @@ class SplashController extends GetxController {
final _loadingProgress = 0.0.obs;
double get loadingProgress => _loadingProgress.value;
// 资源加载状态
final _isResourceLoaded = false.obs;
bool get isResourceLoaded => _isResourceLoaded.value;
final _hasAcceptedPrivacy = false.obs; //是否同意隐私政策
// 版本号
//final String version = 'V1.0.0';
// 主题服务
final ThemeService _themeService = ThemeService();
// 会话级别的版本检查标记(静态变量,整个应用会话期间保持)
static bool _hasCheckedVersionInSession = false;
// 版本检查相关常量
static const String _lastVersionCheckKey = 'last_version_check_timestamp';
static const int _versionCheckIntervalHours = 24; // 24小时间隔
// 使用一个反应式变量来决定是否可以导航,替代所有布尔标记
final _canNavigate = false.obs;
@override
void onInit() {
super.onInit();
// 监听 _canNavigate 状态,一旦为 true,立即且仅执行一次导航
once(_canNavigate, (_) => _navigateToTargetPage());
// 启动整个初始化流程
_initializeApp();
}
/// 启动并协调所有初始化任务
Future<void> _initializeApp() async {
// 步骤 1: 检查并等待隐私政策同意
final hasAccepted = await _checkAndRequestPrivacy();
if (!hasAccepted) {
// 如果用户拒绝隐私政策,应用会退出,后续代码不执行
return;
}
// 首先检查隐私政策状态
final hasAcceptedPrivacy = _storage.read('hasAcceptedPrivacy') ?? false;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!hasAcceptedPrivacy) {
// 优先显示隐私政策弹窗,暂停其他初始化
_showPrivacyDialog();
} else {
// 已同意隐私政策,正常启动流程
_startNormalFlow();
}
});
// 异步处理用户追踪授权,不阻塞主流程
_requestTrackingPermissionAsync();
}
// 步骤 2: 并行执行所有启动任务,并等待它们全部完成
// Future.wait 会等待数组中所有的 Future 都完成后才继续
await Future.wait([
_startLoadingAnimation(), // 任务1: 最小动画时长
_performParallelTasks(), // 任务2: 其他所有后台任务
]);
/// 异步请求追踪权限,不阻塞启动流程
void _requestTrackingPermissionAsync() {
Future.delayed(const Duration(milliseconds: 500), () async {
try {
await AppTrackingTransparency.requestTrackingAuthorization();
Logger.d(_tag, '用户追踪权限请求完成');
} catch (e) {
Logger.e(_tag, '用户追踪权限请求失败: $e');
}
});
// 步骤 3: 所有任务(动画和后台)都已完成,触发导航
_canNavigate.value = true;
}
void _startNormalFlow() {
_startLoadingAnimation();
_loadAppResources();
}
/// 检查隐私政策,如果未同意,则显示弹窗并等待用户操作
/// 返回用户是否同意
Future<bool> _checkAndRequestPrivacy() async {
if (_storage.read<bool>('hasAcceptedPrivacy') ?? false) {
return true;
}
/// 智能调度版本检查,在页面跳转后执行,完全不影响启动流程
void _scheduleVersionCheck() {
// 在独立的微任务中执行,避免阻塞主线程
scheduleMicrotask(() async {
try {
Logger.d(_tag, '开始执行版本检查(后台任务)');
_checkVersionUpdateIfNeeded();
} catch (e) {
Logger.e(_tag, '版本检查调度失败: $e');
}
final completer = Completer<bool>();
WidgetsBinding.instance.addPostFrameCallback((_) {
PrivacyPolicyDialog.show(
onAccept: () {
_storage.write('hasAcceptedPrivacy', true);
Get.back(); // 关闭弹窗
if (!completer.isCompleted) completer.complete(true);
},
onReject: () {
if (!completer.isCompleted) completer.complete(false);
exit(0); // 退出应用
},
canDismiss: false,
);
});
}
void _onPrivacyAccepted() {
_storage.write('hasAcceptedPrivacy', true);
Get.back(); // 关闭弹窗
_startNormalFlow(); // 开始正常流程(已包含版本检查)
}
void _onPrivacyRejected() {
exit(0); // 退出应用
}
// 显示隐私政策弹窗
void _showPrivacyDialog() {
PrivacyPolicyDialog.show(
onAccept: () {
_onPrivacyAccepted();
_hasAcceptedPrivacy.value = true;
},
onReject: _onPrivacyRejected,
canDismiss: false, // 禁止通过返回键关闭弹窗
);
return completer.future;
}
// 进度条动画(持续2秒)
void _startLoadingAnimation() {
const totalDuration = 1500; // 动画总时长增加到1.5秒
const steps = totalDuration ~/ 30; // 30的间隔,更平滑的动画
/// 动画任务:返回一个在动画结束时完成的 Future
Future<void> _startLoadingAnimation() {
const totalDuration = 1500; // 动画总时长1.5秒
const steps = totalDuration ~/ 30;
const stepValue = 1.0 / steps;
final completer = Completer<void>();
Timer.periodic(const Duration(milliseconds: 30), (timer) {
if (_loadingProgress.value < 1.0) {
_loadingProgress.value += stepValue;
} else {
_loadingProgress.value = 1.0;
timer.cancel();
_checkNavigateCondition();
if (!completer.isCompleted) completer.complete();
}
});
}
// 异步加载应用资源
Future<void> _loadAppResources() async {
try {
// 模拟资源加载(如API配置、本地缓存等)
await Future.delayed(const Duration(milliseconds: 1500));
return completer.future;
}
// 实际资源加载代码
// await _loadImages();
// await _initDatabase();
/// 将所有并行的后台任务打包成一个 Future
Future<void> _performParallelTasks() async {
// 同样使用 Future.wait,确保此函数在所有子任务完成后才结束
await Future.wait([
_requestTrackingPermissionAsync(),
_validateTokenAsync(),
_performVersionCheckAsync(),
]);
Logger.d(_tag, '所有后台任务已完成');
}
_isResourceLoaded.value = true;
_checkNavigateCondition();
/// 异步请求追踪权限
Future<void> _requestTrackingPermissionAsync() async {
try {
await AppTrackingTransparency.requestTrackingAuthorization();
Logger.d(_tag, '用户追踪权限请求完成');
} catch (e) {
Get.snackbar('错误', '资源加载失败,请检查网络连接');
_isResourceLoaded.value = true; // 允许用户继续使用
Logger.e(_tag, '资源加载错误: $e');
Logger.e(_tag, '用户追踪权限请求失败: $e');
// 即使失败,也视为完成,不阻塞主流程
}
}
// 检查跳转条件
void _checkNavigateCondition() async {
if (_loadingProgress.value >= 1.0 && _isResourceLoaded.value) {
// 关键改动:先执行页面跳转,并用 await 等待它完成
await _navigateToLogin();
/// 异步验证Token和加载应用配置
Future<void> _validateTokenAsync() async {
try {
final token = _storage.read('logintoken');
if (token != null && token.isNotEmpty) {
final isLoggedIn = await getLoginToken();
if (isLoggedIn) {
// 只有登录成功,才获取App配置
await getAppConfig();
} else {
await User.clearUserSession(); //
}
}
} catch (e) {
Logger.e(_tag, 'Token验证或配置加载失败: $e');
await User.clearUserSession();
}
}
// 页面跳转完全结束后,再安心地安排版本检查
_scheduleVersionCheck();
/// 异步执行版本检查
Future<void> _performVersionCheckAsync() async {
try {
final versionService = VersionUpdateService.to;
final updateInfo = await versionService.checkForUpdate();
if (updateInfo.hasUpdate) {
Logger.i(_tag, '发现新版本: ${updateInfo.latestVersion}');
final completer = Completer<void>();
// 在主线程中显示更新对话框
WidgetsBinding.instance.addPostFrameCallback((_) {
// 使用 whenComplete 确保无论弹窗如何关闭,Future 都会完成
VersionUpdateDialog.show(updateInfo).whenComplete(() {
if (!completer.isCompleted) completer.complete();
});
});
// 等待弹窗交互结束
await completer.future;
} else {
Logger.i(_tag, '当前已是最新版本');
}
} catch (e) {
Logger.e(_tag, '版本检查失败: $e');
// 即使检查失败,也视为任务完成
}
}
// 获取登录Token并跳转
Future<void> _navigateToLogin() async {
bool isLoggedIn = false;
/// 根据最终状态跳转到目标页面
void _navigateToTargetPage() {
// 导航时直接检查最终的数据状态,而不是依赖中间变量
final token = _storage.read('logintoken');
Logger.d(_tag, 'getLoginToken:进来了${token}');
if (token != null && token.isNotEmpty) {
isLoggedIn = await getLoginToken();
}
if (isLoggedIn) {
await getAppConfig();
final isAppConfigInitialized = AppConfig.isInitialized();
// 检查登录状态和配置初始化状态
if (token != null && token.isNotEmpty && isAppConfigInitialized) {
Get.offAllNamed(Routes.pairing);
} else {
Get.offAllNamed(Routes.login); // 跳转到登录页
Get.offAllNamed(Routes.login);
}
}
/// 异步获取登录Token
Future<bool> getLoginToken() async {
try {
Logger.d(_tag, 'getLoginToken:进来了');
//if (User.getname() != "") {
final response = await Api.tokenlogin();
Logger.d(_tag, 'getLoginToken: $response');
if (response == null) return false;
if (response['user'] != '') {
Logger.d(_tag, 'getLoginToken:进来了');
// 根据API设计调整
if (response?['user'] != null && response['user'] != '') {
final user = User.fromJson(response);
// 存储用户信息(示例)
await _storage.write('user_info', user.toJson());
return true;
}
//return true;
//}
return false;
} catch (e) {
Logger.e(_tag, 'Token获取失败: $e');
@ -205,131 +200,14 @@ class SplashController extends GetxController {
Future<bool> getAppConfig() async {
try {
Logger.d(_tag, 'getAppConfig:进来了');
//if (User.getname() != "") {
final response = await Api.getappconfig();
Logger.d(_tag, 'getAppConfig: $response');
if (response == null) return false;
// 根据API设计调整
final resp = UserGetAppConfigResp.fromJson(response);
Logger.d(_tag, 'getAppConfig:进来了');
// 存储用户信息(示例)
// _storage.write('ENV', resp.env);
// _storage.write('AGENTS', resp.agents);
// _storage.write('MCPS', resp.mcps);
AppConfig.initialize(resp);
return true;
} catch (e) {
Logger.e(_tag, 'getAppConfig 取失败: $e');
Logger.e(_tag, 'getAppConfig 获取失败: $e');
return false;
}
}
/// 混合检查机制:判断是否需要进行版本检查
/// 检查条件:1. 会话内未检查过 OR 2. 距离上次检查超过24小时
void _checkVersionUpdateIfNeeded() {
try {
final now = DateTime.now();
final lastCheckTimestamp = _storage.read(_lastVersionCheckKey);
bool shouldCheck = false;
String reason = '';
// 条件1:会话内未检查过
if (!_hasCheckedVersionInSession) {
shouldCheck = true;
reason = '会话内首次检查';
}
// 条件2:距离上次检查超过24小时
else if (lastCheckTimestamp != null) {
final lastCheckTime =
DateTime.fromMillisecondsSinceEpoch(lastCheckTimestamp);
final timeDifference = now.difference(lastCheckTime);
if (timeDifference.inHours >= _versionCheckIntervalHours) {
shouldCheck = true;
reason = '距离上次检查已超过${_versionCheckIntervalHours}小时';
}
}
// 条件3:从未检查过(首次使用应用)
else {
shouldCheck = true;
reason = '首次使用应用';
}
if (shouldCheck) {
Logger.i(_tag, '开始版本检查 - 原因: $reason');
_performVersionCheck();
} else {
Logger.i(_tag, '跳过版本检查 - 会话内已检查且未超过时间间隔');
}
} catch (e) {
Logger.e(_tag, '版本检查判断失败: $e');
}
}
/// 执行版本检查(在后台进行,不阻塞启动流程)
Future<void> _performVersionCheck() async {
try {
final versionService = VersionUpdateService.to;
// 异步执行,不等待结果
versionService.checkForUpdate().then((updateInfo) {
if (updateInfo.hasUpdate) {
Logger.i(_tag, '发现新版本: ${updateInfo.latestVersion}');
// 在主线程中显示更新对话框
WidgetsBinding.instance.addPostFrameCallback((_) {
VersionUpdateDialog.show(updateInfo);
});
} else {
Logger.i(_tag, '当前已是最新版本: ${updateInfo.currentVersion}');
}
// 更新检查记录
_updateVersionCheckRecord();
}).catchError((e) {
Logger.e(_tag, '版本检查失败: $e');
// 即使检查失败,也更新记录,避免频繁重试
_updateVersionCheckRecord();
});
} catch (e) {
Logger.e(_tag, '版本检查失败: $e');
// 即使检查失败,也更新记录,避免频繁重试
_updateVersionCheckRecord();
}
}
/// 更新版本检查记录
void _updateVersionCheckRecord() {
try {
// 更新时间戳
final now = DateTime.now();
_storage.write(_lastVersionCheckKey, now.millisecondsSinceEpoch);
// 标记本次会话已检查过版本
_hasCheckedVersionInSession = true;
Logger.i(_tag, '版本检查记录已更新: ${now.toIso8601String()}');
} catch (e) {
Logger.e(_tag, '更新版本检查记录失败: $e');
}
}
/// 重置版本检查标记(可用于强制重新检查)
/// 注意:这会清除会话标记,但不会清除时间戳记录
static void resetVersionCheckFlag() {
_hasCheckedVersionInSession = false;
Logger.i('SplashCtrl', '版本检查会话标记已重置');
}
/// 完全重置版本检查状态(包括时间戳)
/// 用于测试或特殊情况下强制重新检查
void forceResetVersionCheck() {
try {
_hasCheckedVersionInSession = false;
_storage.remove(_lastVersionCheckKey);
Logger.i(_tag, '版本检查状态已完全重置');
} catch (e) {
Logger.e(_tag, '重置版本检查状态失败: $e');
}
}
}

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

@ -888,7 +888,7 @@ object AgentService : CoroutineScope {
responseBuilder.append(token)
if (speakResponse && reply && broadcast) {
ttsService?.speakStream(token)
audioPlayer?.stopAudio()
// audioPlayer?.stopAudio()
}
if (reply && token.isNotEmpty()) {
// 发送流式回复token

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

@ -996,7 +996,7 @@ class ChatApiStreamCallback: StreamCallback {
if speakResponse && reply && broadcast{
try agentService.azureTtsHelper?.speakStream(token)
try agentService.audioPlayer?.stopAwaitSound()
// try agentService.audioPlayer?.stopAwaitSound()
}
if (reply && token != "") {
try agentService.sendEvent(name: "assistant_token", data: ["token": token])
@ -1142,7 +1142,7 @@ extension AgentServiceImpl: TtsEventListener {
isSpeaking = true
restartIdleCheck()
sendEvent(name: "tts_started", data: ["status": "started"])
audioPlayer?.stopAwaitSound()
case .synthesisCompleted:
isSpeaking = false
restartIdleCheck()

4
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AudioSessionHub.swift

@ -583,9 +583,9 @@ public class AudioSessionHub {
switch action {
case .increase:
newValue = min(currentValue + 0.1, 1.0)
newValue = min(currentValue + 0.2, 1.0) // 从 0.1 改为 0.2 (20%)
case .decrease:
newValue = max(currentValue - 0.1, 0.0)
newValue = max(currentValue - 0.2, 0.0) // 从 0.1 改为 0.2 (20%)
case .mute:
newValue = 0.0
case .setMax:

227
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift

@ -100,14 +100,9 @@ public class AzureAsrHelper: NSObject {
// 创建语音配置
speechConfig = try SPXSpeechConfiguration(subscription: subscriptionKey, region: region)
// 针对翻译场景的性能优化
optimizeForTranslation()
// optimizeForTranslation()
if isAutoDetectLanguage {
// 针对翻译场景优化的语言检测配置
speechConfig?.setPropertyTo("Continuous", byName: "SpeechServiceConnection_LanguageIdMode")
// 设置语言检测的置信度阈值,提高准确性
speechConfig?.setPropertyTo("0.7", byName: "SpeechServiceConnection_AutoDetectSourceLanguageConfidenceThreshold")
// 不在这里设置语言,让SPXAutoDetectSourceLanguageConfiguration处理
os_log("启用语言检测模式,支持语言: %{public}@", log: log, type: .info, supportedLanguages.joined(separator: ","))
} else {
@ -150,55 +145,7 @@ public class AzureAsrHelper: NSObject {
}
}
/**
* 检测输入文本的语言,从支持的语言列表中选择最匹配的
*
* @param text 需要检测的文本
* @return 从supportedLanguages中选择的最匹配语言代码
*/
public func detectTextLanguage(_ text: String) -> String? {
// 如果只有一种支持的语言,直接返回
if supportedLanguages.count == 1 {
return supportedLanguages[0]
}
// 获取所有可能的语言及其置信度
let languageResults = LanguageDetector.detectLanguageWithConfidence(from: text)
// 遍历检测结果,找到第一个在supportedLanguages中的语言
for result in languageResults {
let azureLanguageCode = LanguageDetector.convertToAzureLanguageCode(result.language)
if supportedLanguages.contains(azureLanguageCode) {
return azureLanguageCode
}
}
// 如果没有找到匹配的语言,返回第一个支持的语言作为默认值
return supportedLanguages.first
}
/**
* 检测文本语言并返回详细信息
*
* @param text 需要检测的文本
* @return 包含语言代码、置信度和语言名称的字典
*/
public func detectTextLanguageWithDetails(_ text: String) -> [String: Any]? {
let results = LanguageDetector.detectLanguageWithConfidence(from: text)
guard let topResult = results.first else {
return nil
}
let azureLanguageCode = LanguageDetector.convertToAzureLanguageCode(topResult.language)
let languageName = LanguageDetector.getLanguageName(from: topResult.language)
return [
"languageCode": azureLanguageCode,
"confidence": topResult.confidence,
"languageName": languageName,
"originalCode": topResult.language
]
}
/**
* 开始连续语音识别
*
@ -249,28 +196,7 @@ public class AzureAsrHelper: NSObject {
return false
}
}
// /**
// * 获取检测到的语言
// */
// private func getDetectedLanguage(from result: SPXSpeechRecognitionResult) -> String {
// if !isAutoDetectLanguage {
// return ""
// }
// // 尝试从属性中获取语言
// if let properties = result.properties,
// let language = properties.getPropertyByName("SpeechServiceResponse_RecognitionLanguage") {
// return language
// }
// // 尝试另一种方式获取语言
// do {
// let langResult = try SPXAutoDetectSourceLanguageResult(result)
// return langResult.language ?? ""
// } catch {
// return ""
// }
// }
/**
* 停止连续语音识别
@ -388,11 +314,20 @@ public class AzureAsrHelper: NSObject {
// 创建识别器
if isAutoDetectLanguage {
// 使用正确的初始化方法
let autoDetectConfig = try SPXAutoDetectSourceLanguageConfiguration(supportedLanguages)
print("autoDetectConfig: \(autoDetectConfig)")
guard let autoDetectSourceLanguageConfig = try? SPXAutoDetectSourceLanguageConfiguration(supportedLanguages) else {
print("创建自动语言检测配置失败,使用默认语言配置")
// 如果创建失败,使用默认的单语言配置
recognizer = try SPXSpeechRecognizer(
speechConfiguration: speechConfig!,
audioConfiguration: audioConfig!
)
return true
}
print("autoDetectConfig: \(autoDetectSourceLanguageConfig)")
recognizer = try SPXSpeechRecognizer(
speechConfiguration: speechConfig!,
autoDetectSourceLanguageConfiguration: autoDetectConfig,
autoDetectSourceLanguageConfiguration: autoDetectSourceLanguageConfig,
audioConfiguration: audioConfig!
)
} else {
@ -484,60 +419,7 @@ public class AzureAsrHelper: NSObject {
return currentLanguage
}
/**
* 针对翻译场景的性能优化配置
*/
private func optimizeForTranslation() {
guard let speechConfig = speechConfig else { return }
// 启用实时转录,减少延迟
speechConfig.setPropertyTo("true", byName: "SpeechServiceConnection_EnableAudioLogging")
speechConfig.setPropertyTo("INTERACTIVE", byName: "SpeechServiceConnection_RecoMode")
speechConfig.setPropertyTo("PCM", byName: "Speech_PushStreamFormat")
// 优化语音端点检测,适合翻译场景
speechConfig.setPropertyTo("300", byName: "SpeechServiceConnection_InitialSilenceTimeoutMs")
speechConfig.setPropertyTo("500", byName: "SpeechServiceConnection_EndSilenceTimeoutMs")
speechConfig.setPropertyTo("500", byName: "Speech_SegmentationSilenceTimeoutMs")
// 启用标点符号,提高翻译质量
speechConfig.setPropertyTo("true", byName: "SpeechServiceResponse_RequestProfanityFilterTrueFalse")
// 设置分段策略为时间模式
speechConfig.setPropertyTo("Time", byName: "Speech_SegmentationStrategy")
// 【优化】语言检测配置 - 减少检测延迟,实现快速响应
if isAutoDetectLanguage {
// 降低语言检测置信度阈值,提高检测灵敏度
speechConfig.setPropertyTo("0.3", byName: "SpeechServiceConnection_AutoDetectSourceLanguageConfidenceThreshold")
// 使用Continuous模式,在每句话结束后重新检测语言
speechConfig.setPropertyTo("Continuous", byName: "SpeechServiceConnection_LanguageIdMode")
// 启用快速语言检测
speechConfig.setPropertyTo("true", byName: "SpeechServiceConnection_EnableLanguageDetectionFast")
// 【关键优化】设置语言检测的最小音频长度为100毫秒,实现快速响应
speechConfig.setPropertyTo("100", byName: "SpeechServiceConnection_LanguageDetectionMinAudioLengthMs")
// 【新增】设置语言检测的最大等待时间,避免长时间等待
speechConfig.setPropertyTo("2000", byName: "SpeechServiceConnection_LanguageDetectionMaxWaitTimeMs")
// 强制在每个语音段开始时重新检测语言
speechConfig.setPropertyTo("true", byName: "SpeechServiceConnection_ForceLanguageDetectionOnNewSegment")
// 【新增】启用句子级别的语言检测
speechConfig.setPropertyTo("true", byName: "SpeechServiceConnection_EnableLanguageDetectionPerSentence")
// 【新增】设置语言检测的频率 - 每个识别结果后都检测
speechConfig.setPropertyTo("PerResult", byName: "SpeechServiceConnection_LanguageDetectionFrequency")
// 【新增】启用实时语言检测,减少缓冲延迟
speechConfig.setPropertyTo("true", byName: "SpeechServiceConnection_EnableRealtimeLanguageDetection")
// 【新增】设置语言检测的采样间隔为200毫秒
speechConfig.setPropertyTo("200", byName: "SpeechServiceConnection_LanguageDetectionSamplingIntervalMs")
}
os_log("翻译场景优化配置已应用,包含语言检测优化", log: log, type: .info)
}
/**
* 动态切换语言(翻译场景中的语言切换)
@ -588,13 +470,16 @@ public class AzureAsrHelper: NSObject {
let result = event.result
guard !(result.text?.isEmpty ?? true) else { return }
// let detectedLanguage = self.getDetectedLanguage(from: result)
// 检测文本语言,避免重复调用
let detectedLanguage = self.detectTextLanguage(result.text ?? "") ?? "zh-CN"
// 使用现有的 getDetectedLanguage 方法替代 result.language
let detectedLanguage = self.getDetectedLanguage(from: result)
let text = result.text
print("正在识别事件=检测到语言: \(detectedLanguage), 识别中: \(text)")
// // 检测文本语言,避免重复调用
// let detectedLanguage = self.detectTextLanguage(result.text ?? "") ?? "zh-CN"
// 输出检测到的语言信息
print("检测到的语言: \(detectedLanguage)")
// // 输出检测到的语言信息
// print("检测到的语言: \(detectedLanguage)")
DispatchQueue.main.async {
callback.onRecognizing(result.text ?? "", detectedLanguage)
@ -608,11 +493,16 @@ public class AzureAsrHelper: NSObject {
guard result.reason == .recognizedSpeech,
!(result.text?.isEmpty ?? true) else { return }
// 使用现有的 getDetectedLanguage 方法替代 result.language
let detectedLanguage = self.getDetectedLanguage(from: result)
let text = result.text
print("识别完成事件=检测到语言: \(detectedLanguage), 识别中: \(text)")
// 检测文本语言,避免重复调用
let detectedLanguage = self.detectTextLanguage(result.text ?? "") ?? "zh-CN"
// let detectedLanguage = self.detectTextLanguage(result.text ?? "") ?? "zh-CN"
// 输出检测到的语言信息
print("检测到的语言: \(detectedLanguage)")
// // 输出检测到的语言信息
// print("检测到的语言: \(detectedLanguage)")
// 在主线程回调结果
DispatchQueue.main.async {
@ -1175,3 +1065,54 @@ private func runMicrophoneCapture() {
func onError(_ error: String)
}
}
// //ios 语言检测
// /**
// * 检测输入文本的语言,从支持的语言列表中选择最匹配的
// *
// * @param text 需要检测的文本
// * @return 从supportedLanguages中选择的最匹配语言代码
// */
// public func detectTextLanguage(_ text: String) -> String? {
// // 如果只有一种支持的语言,直接返回
// if supportedLanguages.count == 1 {
// return supportedLanguages[0]
// }
// // 获取所有可能的语言及其置信度
// let languageResults = LanguageDetector.detectLanguageWithConfidence(from: text)
// // 遍历检测结果,找到第一个在supportedLanguages中的语言
// for result in languageResults {
// let azureLanguageCode = LanguageDetector.convertToAzureLanguageCode(result.language)
// if supportedLanguages.contains(azureLanguageCode) {
// return azureLanguageCode
// }
// }
// // 如果没有找到匹配的语言,返回第一个支持的语言作为默认值
// return supportedLanguages.first
// }
// /**
// * 检测文本语言并返回详细信息
// *
// * @param text 需要检测的文本
// * @return 包含语言代码、置信度和语言名称的字典
// */
// public func detectTextLanguageWithDetails(_ text: String) -> [String: Any]? {
// let results = LanguageDetector.detectLanguageWithConfidence(from: text)
// guard let topResult = results.first else {
// return nil
// }
// let azureLanguageCode = LanguageDetector.convertToAzureLanguageCode(topResult.language)
// let languageName = LanguageDetector.getLanguageName(from: topResult.language)
// return [
// "languageCode": azureLanguageCode,
// "confidence": topResult.confidence,
// "languageName": languageName,
// "originalCode": topResult.language
// ]
// }

77
local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift

@ -28,6 +28,7 @@ public protocol FunctionHandler {
/// - 安全的JSON-RPC消息解析,包含错误修复逻辑
/// - 完整的SSE连接生命周期管理
/// - 与Android端CustomSseClientTransport功能对等
// 在 MCPSubClient 类中添加工具调用超时和重试配置
public class MCPSubClient {
private let serverId: String
private let serverUrl: String
@ -47,6 +48,10 @@ public class MCPSubClient {
private var retryCount = 0
private var reconnectTask: Task<Void, Never>?
// 工具调用配置 - 添加这两行
private let maxToolCallRetries = 3
private let toolCallTimeout: TimeInterval = 30.0
// 连接状态锁
private let connectionLock = NSLock()
@ -178,27 +183,55 @@ public class MCPSubClient {
/// 调用工具前检查连接
public func callTool(name: String, arguments: [String: Any]) async -> [String: Any]? {
return await callToolWithRetry(name: name, arguments: arguments, retryCount: 0)
}
private func callToolWithRetry(name: String, arguments: [String: Any], retryCount: Int) async -> [String: Any]? {
print("[MCPSubClient] 工具调用尝试 \(retryCount + 1)/\(maxToolCallRetries + 1): \(name)")
// 先检查连接状态
if !(await checkConnection()) {
if retryCount < maxToolCallRetries {
print("[MCPSubClient] 连接断开,尝试重连...")
if await connect() {
print("[MCPSubClient] 重连成功,重试工具调用")
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1)
}
}
return [
"content": [[
"type": "text",
"text": "Tool call failed: mcp: service not connected"
"text": "工具调用失败,请重试:MCP 服务连接中断"
]],
"isError": true
]
}
guard isConnectedFlag, let client = mcpClient else {
return createErrorResult("Not connected to MCP server")
if retryCount < maxToolCallRetries {
print("[MCPSubClient] MCP 客户端未连接,尝试重连...")
if await connect() {
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1)
}
}
return createErrorResult("工具调用失败,请重试:MCP 服务未连接")
}
do {
let mcpArguments = convertToMCPValue(arguments)
let (content, isError) = try await client.callTool(name: name, arguments: mcpArguments)
// 使用超时控制的工具调用
let result = try await withTimeout(seconds: toolCallTimeout) {
let mcpArguments = self.convertToMCPValue(arguments)
return try await client.callTool(name: name, arguments: mcpArguments)
}
let (content, isError) = result
if isError == true {
return createErrorResult("Tool execution failed")
if retryCount < maxToolCallRetries {
print("[MCPSubClient] 工具执行失败,重试中...")
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1)
}
return createErrorResult("工具调用失败,请重试:工具执行错误")
}
var contextText = ""
@ -225,13 +258,45 @@ public class MCPSubClient {
}
}
print("[MCPSubClient] 工具调用成功: \(name)")
return [
"content": [["type": "text", "text": contextText.trimmingCharacters(in: .whitespacesAndNewlines)]],
"isError": false
]
} catch is TimeoutError {
print("[MCPSubClient] 工具调用超时: \(name)")
if retryCount < maxToolCallRetries {
print("[MCPSubClient] 超时重试中...")
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1)
}
return createErrorResult("工具调用失败,请重试:连接超时")
} catch {
return createErrorResult("Tool call failed: \(error.localizedDescription)")
print("[MCPSubClient] 工具调用异常: \(name), 错误: \(error.localizedDescription)")
// 检查是否是连接相关错误
let errorDescription = error.localizedDescription.lowercased()
if errorDescription.contains("connection") ||
errorDescription.contains("network") ||
errorDescription.contains("timeout") ||
errorDescription.contains("disconnected") {
if retryCount < maxToolCallRetries {
print("[MCPSubClient] 检测到连接错误,尝试重连并重试...")
// 标记连接断开
isConnectedFlag = false
// 尝试重连
if await connect() {
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1)
}
}
} else if retryCount < maxToolCallRetries {
print("[MCPSubClient] 其他错误,直接重试...")
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1)
}
return createErrorResult("工具调用失败,请重试:\(error.localizedDescription)")
}
}

18
local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift

@ -795,7 +795,7 @@ private class GetCurrentTimeHandler: FunctionHandler {
private class MediaPlayHandler: FunctionHandler {
func handle(arguments: [String: Any]) async throws -> String {
NotificationCenter.default.post(name: NSNotification.Name("MediaPlay"), object: nil)
return "{\"result\": \"已开始播放媒体\"}"
return "{\"notreply\": true,\"result\": \"已开始播放媒体\"}"
}
}
@ -803,7 +803,7 @@ private class MediaPlayHandler: FunctionHandler {
private class MediaPauseHandler: FunctionHandler {
func handle(arguments: [String: Any]) async throws -> String {
NotificationCenter.default.post(name: NSNotification.Name("MediaPause"), object: nil)
return "{\"result\": \"已暂停媒体播放\"}"
return "{\"broadcast\": true,\"result\": \"已暂停媒体播放\"}"
}
}
@ -811,7 +811,7 @@ private class MediaPauseHandler: FunctionHandler {
private class MediaPreviousHandler: FunctionHandler {
func handle(arguments: [String: Any]) async throws -> String {
NotificationCenter.default.post(name: NSNotification.Name("MediaPrevious"), object: nil)
return "{\"result\": \"已切换到上一首\"}"
return "{\"notreply\": true,\"result\": \"已切换到上一首\"}"
}
}
@ -819,7 +819,7 @@ private class MediaPreviousHandler: FunctionHandler {
private class MediaNextHandler: FunctionHandler {
func handle(arguments: [String: Any]) async throws -> String {
NotificationCenter.default.post(name: NSNotification.Name("MediaNext"), object: nil)
return "{\"result\": \"已切换到下一首\"}"
return "{\"notreply\": true,\"result\": \"已切换到下一首\"}"
}
}
@ -833,7 +833,7 @@ private class OpenRecorderHandler: FunctionHandler {
}
}
return "{\"result\": \"无法打开录音机应用\"}"
return "{\"notreply\": true,\"result\": \"无法打开录音机应用\"}"
}
}
@ -905,7 +905,7 @@ private class AddVolumeHandler: FunctionHandler {
object: volumeInfo
)
return "{\"result\": \"音量增加请求已发送\"}"
return "{\"broadcast\": false,\"result\": \"音量增加请求已发送\"}"
}
}
@ -921,7 +921,7 @@ private class ReduceVolumeHandler: FunctionHandler {
object: volumeInfo
)
return "{\"result\": \"音量降低请求已发送\"}"
return "{\"broadcast\": false,\"result\": \"音量降低请求已发送\"}"
}
}
@ -937,7 +937,7 @@ private class MuteVolumeHandler: FunctionHandler {
object: volumeInfo
)
return "{\"result\": \"静音请求已发送\"}"
return "{\"broadcast\": false,\"result\": \"静音请求已发送\"}"
}
}
@ -953,6 +953,6 @@ private class MaxVolumeHandler: FunctionHandler {
object: volumeInfo
)
return "{\"result\": \"最大音量请求已发送\"}"
return "{\"broadcast\": false,\"result\": \"最大音量请求已发送\"}"
}
}

Loading…
Cancel
Save