Browse Source

上传配置相关逻辑

weicu
liwei1dao 1 year ago
parent
commit
0258fbafa8
  1. 28
      lib/data/models/appconfig.dart
  2. 30
      lib/data/services/ble_manager.dart
  3. 9
      lib/data/services/open_ai_service_adapter.dart
  4. 36
      lib/data/services/speech_impl/azure_asr_service.dart
  5. 74
      lib/data/services/speech_impl/azure_tts_service.dart
  6. 9
      lib/data/services/spotify_service.dart
  7. 14
      lib/data/services/volcano_translation_service.dart
  8. 8
      lib/modules/login/controllers/login_controller.dart
  9. 9
      lib/modules/splash/controllers/splash_controller.dart
  10. 2
      local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt
  11. 4
      local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/OpenAIService.kt

28
lib/data/models/appconfig.dart

@ -0,0 +1,28 @@
import 'dart:convert';
import 'package:deep_voice/data/models/appconfig_model.dart';
class AppConfig {
static late UserGetAppConfigResp config;
// 可选:提供初始化方法
static void initialize(UserGetAppConfigResp resp) {
config = resp;
}
//环境变量
static String? env(String key) {
return config.env[key];
}
//mcp配置
static dynamic mcpConfig() {
return {"mcpServers": config.mcps};
}
//智能体配置
static String agent(String agentId) {
final foundAgent = config.agents.firstWhere((agent) => agent.id == agentId);
return jsonEncode((foundAgent));
}
}

30
lib/data/services/ble_manager.dart

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:get/get.dart';
import 'package:ble_service/ble_service.dart';
import 'package:deep_voice/core/utils/logger.dart';
@ -383,17 +384,28 @@ class BleManager extends GetxService {
Future<bool> startBleService() async {
try {
// 从.env文件直接读取环境变量
final azureSpeechKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
final azureSpeechRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? 'eastasia';
final openaiApiKey = dotenv.env['OPENAI_API_KEY'] ?? '';
// final azureSpeechKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
// final azureSpeechRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? 'eastasia';
// final openaiApiKey = dotenv.env['OPENAI_API_KEY'] ?? '';
// final openaiBaseUrl =
// dotenv.env['OPENAI_BASE_URL'] ?? 'https://api.openai.com/v1';
// final openaiModel = dotenv.env['OPENAI_MODEL'] ?? 'gpt-3.5-turbo';
// 从Flutter资源包中加载.mcp.json
// final mcpConfig = await rootBundle.loadString('.mcp.json');
// 验证JSON格式
// final jsonData = jsonDecode(mcpConfig);
final azureSpeechKey = AppConfig.env('AZURE_SPEECH_KEY') ?? '';
final azureSpeechRegion =
AppConfig.env('AZURE_SPEECH_REGION') ?? 'eastasia';
final openaiApiKey = AppConfig.env('OPENAI_API_KEY') ?? '';
final openaiBaseUrl =
dotenv.env['OPENAI_BASE_URL'] ?? 'https://api.openai.com/v1';
final openaiModel = dotenv.env['OPENAI_MODEL'] ?? 'gpt-3.5-turbo';
// 从Flutter资源包中加载.mcp.json
final mcpConfig = await rootBundle.loadString('.mcp.json');
AppConfig.env('OPENAI_BASE_URL') ?? 'https://api.openai.com/v1';
final openaiModel = AppConfig.env('OPENAI_MODEL') ?? 'gpt-3.5-turbo';
// 从Flutter资源包中加载.mcp.json
final jsonData = AppConfig.mcpConfig();
// 验证JSON格式
final jsonData = jsonDecode(mcpConfig);
final config = <String, dynamic>{
// 语音代理配置参数
'azureSpeechKey': azureSpeechKey,

9
lib/data/services/open_ai_service_adapter.dart

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:deep_voice/data/models/appconfig_model.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get_storage/get_storage.dart';
@ -103,10 +104,10 @@ class OpenAIServiceAdapter implements AiService {
// final apiKey = dotenv.env['OPENAI_API_KEY'] ?? '';
// final baseUrl = dotenv.env['OPENAI_BASE_URL'] ?? '';
// final model = dotenv.env['OPENAI_MODEL'] ?? '';
final _env = _storage.read("ENV") as Map<String, String>;
final apiKey = _env['OPENAI_API_KEY'] ?? '';
final baseUrl = _env['OPENAI_BASE_URL'] ?? '';
final model = _env['OPENAI_MODEL'] ?? '';
// final _env = _storage.read("ENV") as Map<String, String>;
final apiKey = AppConfig.env('OPENAI_API_KEY') ?? '';
final baseUrl = AppConfig.env('OPENAI_BASE_URL') ?? '';
final model = AppConfig.env('OPENAI_MODEL') ?? '';
printInfo(info: '从.env读取OpenAI配置');
printInfo(info: '基础URL: $baseUrl');

36
lib/data/services/speech_impl/azure_asr_service.dart

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:flutter/services.dart';
import 'package:get_storage/get_storage.dart';
import '../../../core/utils/logger.dart';
@ -20,7 +21,7 @@ class AzureAsrService extends GetxService implements AsrService {
static const MethodChannel _channel = MethodChannel('azure_speech/asr');
static const EventChannel _eventChannel =
EventChannel('azure_speech/asr_events');
final GetStorage _storage = GetStorage();
// final GetStorage _storage = GetStorage();
bool _isInitialized = false;
late final String _subscriptionKey;
late final String _serviceRegion;
@ -51,9 +52,9 @@ class AzureAsrService extends GetxService implements AsrService {
/// 从环境变量加载配置
void _loadConfig() {
final _env = _storage.read("ENV") as Map<String, String>;
_subscriptionKey = _env['AZURE_SPEECH_KEY'] ?? '';
_serviceRegion = _env['AZURE_SPEECH_REGION'] ?? '';
// final _env = _storage.read("ENV") as Map<String, String>;
_subscriptionKey = AppConfig.env('AZURE_SPEECH_KEY') ?? '';
_serviceRegion = AppConfig.env('AZURE_SPEECH_REGION') ?? '';
if (_subscriptionKey.isEmpty || _serviceRegion.isEmpty) {
throw Exception(
@ -312,4 +313,31 @@ class AzureAsrService extends GetxService implements AsrService {
_isInitialized = false;
}
}
@override
Future<bool> pushAudioData(Uint8List data) async {
if (!_isInitialized) {
await initialize(useExternalAudio: true);
}
if (_audioSourceType != AudioSourceType.external) {
Logger.error('当前非外部音频模式,不能推送音频数据');
return false;
}
try {
final bool result = await _channel.invokeMethod('pushAudioData', {
'audioData': data // Flutter 会自动将 Uint8List 转换为 ByteBuffer
});
if (!result) {
throw Exception('推送音频数据失败');
}
return true;
} catch (e) {
Logger.error('推送音频数据失败: $e');
return false;
}
}
}

74
lib/data/services/speech_impl/azure_tts_service.dart

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
@ -13,22 +14,24 @@ import '../tts_service.dart';
class AzureTtsService extends GetxService implements TtsService {
static final AzureTtsService to = Get.put(AzureTtsService());
static const MethodChannel _channel = MethodChannel('azure_speech/tts');
static const EventChannel _eventChannel = EventChannel('azure_speech/tts_events');
final GetStorage _storage = GetStorage();
static const EventChannel _eventChannel =
EventChannel('azure_speech/tts_events');
// final GetStorage _storage = GetStorage();
bool _isInitialized = false;
late final String _subscriptionKey;
late final String _serviceRegion;
// 当前设置
String _currentVoice = 'zh-CN-XiaoxiaoNeural';
// 语音合成队列
final List<String> _textQueue = [];
bool _isProcessingQueue = false;
// 事件流
final StreamController<TtsEvent> _eventStreamController = StreamController<TtsEvent>.broadcast();
final StreamController<TtsEvent> _eventStreamController =
StreamController<TtsEvent>.broadcast();
StreamSubscription? _eventSubscription;
// 可观察状态
@ -53,9 +56,9 @@ class AzureTtsService extends GetxService implements TtsService {
/// 从环境变量加载配置
void _loadConfig() {
final _env = _storage.read("ENV") as Map<String, String>;
_subscriptionKey = _env['AZURE_SPEECH_KEY'] ?? '';
_serviceRegion = _env['AZURE_SPEECH_REGION'] ?? '';
// final _env = _storage.read("ENV") as Map<String, String>;
_subscriptionKey = AppConfig.env('AZURE_SPEECH_KEY') ?? '';
_serviceRegion = AppConfig.env('AZURE_SPEECH_REGION') ?? '';
}
/// 设置事件通道
@ -74,50 +77,50 @@ class AzureTtsService extends GetxService implements TtsService {
/// 处理TTS事件
void _handleTtsEvent(Map<dynamic, dynamic> event) {
final String eventType = event['type'] as String? ?? '';
// Logger.debug('收到TTS事件: $eventType, 数据: $event');
switch (eventType) {
case 'synthesis_started':
_isSpeaking.value = true;
_eventStreamController.add(TtsEvent(type: TtsEventType.started));
break;
case 'synthesizing':
// _isSpeaking.value = true;
// // 合成中事件暂时映射到started类型,因为TtsEventType中没有synthesizing类型
// _eventStreamController.add(TtsEvent(type: TtsEventType.started));
break;
case 'synthesis_completed':
_isSpeaking.value = false;
_eventStreamController.add(TtsEvent(type: TtsEventType.completed));
// 如果有队列在处理中,继续处理下一项
if (_isProcessingQueue) {
_processNextInQueue();
}
break;
case 'synthesis_canceled':
_isSpeaking.value = false;
final String reason = event['reason'] as String? ?? '';
_eventStreamController.add(TtsEvent.error('合成取消: $reason'));
// 如果有队列在处理中,继续处理下一项(即使出错)
if (_isProcessingQueue) {
_processNextInQueue();
}
break;
case 'error':
_isSpeaking.value = false;
final String message = event['message'] as String? ?? '未知错误';
Logger.error('TTS错误: $message');
_eventStreamController.add(TtsEvent.error(message));
// 如果有队列在处理中,继续处理下一项(即使出错)
if (_isProcessingQueue) {
_processNextInQueue();
@ -189,7 +192,7 @@ class AzureTtsService extends GetxService implements TtsService {
final result = await _channel.invokeMethod('speakText', {
'text': text,
});
return result == true;
} catch (e) {
Logger.error('语音合成失败: $e');
@ -277,21 +280,24 @@ class AzureTtsService extends GetxService implements TtsService {
// 等待队列处理完成
final completer = Completer<bool>();
// 监听事件流检查队列是否完成
final subscription = onEvent.listen((event) {
// 当队列为空且不在处理中,且当前非说话状态,完成操作
if (_textQueue.isEmpty && !_isProcessingQueue && !_isSpeaking.value && !completer.isCompleted) {
if (_textQueue.isEmpty &&
!_isProcessingQueue &&
!_isSpeaking.value &&
!completer.isCompleted) {
completer.complete(true);
}
});
// 如果已经处于空闲状态,直接完成
if (_textQueue.isEmpty && !_isProcessingQueue && !_isSpeaking.value) {
subscription.cancel();
return true;
}
// 设置超时
final timeout = Timer(const Duration(seconds: 60), () {
if (!completer.isCompleted) {
@ -299,14 +305,14 @@ class AzureTtsService extends GetxService implements TtsService {
completer.complete(false);
}
});
// 等待完成
final result = await completer.future;
// 清理
subscription.cancel();
timeout.cancel();
Logger.info('TTS队列播放完成');
return result;
} catch (e) {
@ -320,11 +326,11 @@ class AzureTtsService extends GetxService implements TtsService {
if (_textQueue.isEmpty || _isProcessingQueue) return;
_isProcessingQueue = true;
// 处理队列中的第一项
await _processNextInQueue();
}
/// 处理队列中的下一项
Future<void> _processNextInQueue() async {
// 检查队列是否为空或已禁用
@ -333,14 +339,14 @@ class AzureTtsService extends GetxService implements TtsService {
_isProcessingQueue = false;
return;
}
try {
// 获取当前项但暂不从队列中移除
final text = _textQueue.first;
// 开始合成
final success = await speakOnce(text);
// 仅当合成操作失败时才从队列移除并继续处理下一项
// 成功的话会等待合成完成事件后自动处理下一项
if (!success) {
@ -353,12 +359,12 @@ class AzureTtsService extends GetxService implements TtsService {
}
} catch (e) {
Logger.error('处理语音队列出错: $e');
// 出错时移除当前项并尝试继续
if (_textQueue.isNotEmpty) {
_textQueue.removeAt(0);
}
// 如果队列中还有项目,继续处理
if (_textQueue.isNotEmpty) {
await _processNextInQueue();

9
lib/data/services/spotify_service.dart

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get_storage/get_storage.dart';
import 'package:spotify_sdk/spotify_sdk.dart';
@ -12,7 +13,7 @@ import 'package:path_provider/path_provider.dart';
/// OpenAI服务适配器 - 连接AiService接口与OpenAIService插件
class SpotifService {
bool isInitialized = false;
final GetStorage _storage = GetStorage();
// final GetStorage _storage = GetStorage();
late String SPOTIFY_CLIENT_ID;
late String SPOTIFY_REDIRECT_URL;
@ -20,9 +21,9 @@ class SpotifService {
/// 初始化OpenAI服务
Future<bool> initialize() async {
if (!isInitialized) {
final _env = _storage.read("ENV") as Map<String, String>;
SPOTIFY_CLIENT_ID = _env['SPOTIFY_CLIENT_ID'] ?? '';
SPOTIFY_REDIRECT_URL = _env['SPOTIFY_REDIRECT_URL'] ?? '';
// final _env = _storage.read("ENV") as Map<String, String>;
SPOTIFY_CLIENT_ID = AppConfig.env('SPOTIFY_CLIENT_ID') ?? '';
SPOTIFY_REDIRECT_URL = AppConfig.env('SPOTIFY_REDIRECT_URL') ?? '';
printInfo(
info:
"SpotifService----------- initialize SPOTIFY_CLIENT_ID:${SPOTIFY_CLIENT_ID} SPOTIFY_REDIRECT_URL:${SPOTIFY_REDIRECT_URL}");

14
lib/data/services/volcano_translation_service.dart

@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:math';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart' as http;
import 'package:crypto/crypto.dart';
@ -22,7 +23,7 @@ class VolcanoTranslationService extends GetxService {
// 语言管理器
final LanguageManager _languageManager = Get.find<LanguageManager>();
final GetStorage _storage = GetStorage();
// final GetStorage _storage = GetStorage();
// 构造函数
VolcanoTranslationService();
@ -33,10 +34,13 @@ class VolcanoTranslationService extends GetxService {
String? region,
}) async {
try {
final _env = _storage.read("ENV") as Map<String, String>;
_accessKey = accessKey ?? _env['VOLCANO_TRANSLATION_ACCESS_KEY'] ?? '';
_secretKey = secretKey ?? _env['VOLCANO_TRANSLATION_SECRET_KEY'] ?? '';
_region = region ?? _env['VOLCANO_TRANSLATION_REGION'] ?? 'cn-north-1';
// final _env = _storage.read("ENV") as Map<String, String>;
_accessKey =
accessKey ?? AppConfig.env('VOLCANO_TRANSLATION_ACCESS_KEY') ?? '';
_secretKey =
secretKey ?? AppConfig.env('VOLCANO_TRANSLATION_SECRET_KEY') ?? '';
_region =
region ?? AppConfig.env('VOLCANO_TRANSLATION_REGION') ?? 'cn-north-1';
_service = 'translate';
_baseUrl = 'https://translate.volcengineapi.com';

8
lib/modules/login/controllers/login_controller.dart

@ -1,3 +1,4 @@
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:deep_voice/data/models/appconfig_model.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
@ -739,9 +740,10 @@ class LoginController extends GetxController {
// 根据API设计调整
final resp = UserGetAppConfigResp.fromJson(response);
// 存储用户信息(示例)
_storage.write('ENV', resp.env);
_storage.write('AGENTS', resp.agents);
_storage.write('MCPS', resp.mcps);
// _storage.write('ENV', resp.env);
// _storage.write('AGENTS', resp.agents);
// _storage.write('MCPS', resp.mcps);
AppConfig.initialize(resp);
return true;
} catch (e) {
print('getAppConfig 取失败: $e');

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

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:amap_flutter/amap_flutter.dart';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:deep_voice/data/models/appconfig_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@ -132,10 +133,10 @@ class SplashController extends GetxController {
final resp = UserGetAppConfigResp.fromJson(response);
print('getAppConfig:进来了');
// 存储用户信息(示例)
_storage.write('ENV', resp.env);
_storage.write('AGENTS', resp.agents);
_storage.write('MCPS', resp.mcps);
// _storage.write('ENV', resp.env);
// _storage.write('AGENTS', resp.agents);
// _storage.write('MCPS', resp.mcps);
AppConfig.initialize(resp);
return true;
} catch (e) {
print('getAppConfig 取失败: $e');

2
local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt

@ -313,7 +313,7 @@ class MCPSubClient(
"function" to functionMap
))
Log.d(TAG, "成功转换工具 ${tool.name()} 到OpenAI函数格式")
Log.d(TAG, "成功转换工具 ${tool.name()} 到OpenAI函数格式 $functionMap")
} catch (e: Exception) {
Log.e(TAG, "转换工具 ${tool.name()} 失败: ${e.message}")
}

4
local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/OpenAIService.kt

@ -337,6 +337,8 @@ class OpenAIService(private val context: Context? = null) {
// 获取参数
val argumentsJson = functionCall.getString("arguments")
Log.e(TAG, "自动处理MCP工具调用: name:${name} argumentsJson:${argumentsJson}")
val arguments = mcpClient?.parseJsonArguments(argumentsJson) ?: mapOf()
// 调用工具(统一通过MCPClient处理)
@ -607,7 +609,7 @@ class OpenAIService(private val context: Context? = null) {
for (i in 0 until toolCalls.length()) {
val toolCall = toolCalls.getJSONObject(i)
val index = toolCall.getInt("index")
Log.e(TAG, "解析道AI需要调用工具: $toolCall")
// 创建或获取现有的工具调用信息
val toolCallInfo = finalToolCalls.getOrPut(index) { ToolCallInfo() }

Loading…
Cancel
Save