14 changed files with 695 additions and 277 deletions
@ -0,0 +1,95 @@ |
|||
import 'package:get/get.dart'; |
|||
import 'dart:async'; |
|||
import 'volcano_ai_service.dart'; |
|||
import 'volcano_tts_service.dart'; |
|||
import '../../modules/chat/models/message_model.dart'; |
|||
|
|||
class BackgroundAgentService extends GetxService { |
|||
static BackgroundAgentService get to => Get.find(); |
|||
|
|||
final VolcanoAIService _aiService; |
|||
final VolcanoTtsService _ttsService; |
|||
final List<Message> _messageHistory = []; |
|||
String _pendingTtsText = ''; |
|||
static const int _minTtsLength = 20; |
|||
bool _isProcessing = false; |
|||
|
|||
BackgroundAgentService() |
|||
: _aiService = VolcanoAIService(), |
|||
_ttsService = Get.find<VolcanoTtsService>(); |
|||
|
|||
Future<void> handleAgentInteraction(String systemPrompt) async { |
|||
if (_isProcessing) return; |
|||
_isProcessing = true; |
|||
|
|||
try { |
|||
// 构建用于生成回应的消息列表 |
|||
final messages = [ |
|||
{'role': 'system', 'content': systemPrompt}, |
|||
{'role': 'user', 'content': '请用一句简短的话回应我,要体现你的特点,不要超过12个字。'}, |
|||
]; |
|||
|
|||
String fullResponse = ''; |
|||
await for (final chunk in _aiService.sendMessageStream( |
|||
messages: messages, |
|||
systemPrompt: systemPrompt, |
|||
)) { |
|||
fullResponse += chunk; |
|||
|
|||
// 累积文本并处理TTS |
|||
_pendingTtsText += chunk; |
|||
if (_ttsService.isEnabled.value) { |
|||
if (_pendingTtsText.length >= _minTtsLength) { |
|||
int lastSentenceEnd = _findLastSentenceEnd(_pendingTtsText); |
|||
if (lastSentenceEnd > 0) { |
|||
String textToSpeak = _pendingTtsText.substring(0, lastSentenceEnd + 1); |
|||
await _ttsService.speak(textToSpeak); |
|||
_pendingTtsText = _pendingTtsText.substring(lastSentenceEnd + 1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 处理剩余的文本 |
|||
if (_ttsService.isEnabled.value && _pendingTtsText.isNotEmpty) { |
|||
await _ttsService.speak(_pendingTtsText); |
|||
} |
|||
_pendingTtsText = ''; |
|||
|
|||
// 保存对话历史 |
|||
_messageHistory.add(Message( |
|||
role: 'assistant', |
|||
content: fullResponse, |
|||
timestamp: DateTime.now(), |
|||
)); |
|||
|
|||
// 限制历史记录长度 |
|||
if (_messageHistory.length > 10) { |
|||
_messageHistory.removeAt(0); |
|||
} |
|||
} catch (e) { |
|||
print('Background agent interaction failed: $e'); |
|||
} finally { |
|||
_isProcessing = false; |
|||
} |
|||
} |
|||
|
|||
int _findLastSentenceEnd(String text) { |
|||
final sentenceEnds = [ |
|||
text.lastIndexOf('。'), |
|||
text.lastIndexOf('!'), |
|||
text.lastIndexOf('?'), |
|||
text.lastIndexOf('.'), |
|||
text.lastIndexOf('!'), |
|||
text.lastIndexOf('?'), |
|||
]; |
|||
|
|||
return sentenceEnds.reduce((max, pos) => pos > max ? pos : max); |
|||
} |
|||
|
|||
List<Message> get messageHistory => List.unmodifiable(_messageHistory); |
|||
|
|||
void clearHistory() { |
|||
_messageHistory.clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,147 @@ |
|||
import 'dart:io'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; |
|||
import 'package:device_info_plus/device_info_plus.dart'; |
|||
import 'package:permission_handler/permission_handler.dart'; |
|||
import '../providers/agent_provider.dart'; |
|||
|
|||
class NotificationService extends GetxService { |
|||
static NotificationService get to => Get.find(); |
|||
|
|||
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); |
|||
bool _notificationActive = false; |
|||
bool _isRequestingPermission = false; |
|||
|
|||
String get _defaultAgentName { |
|||
final agent = AgentProvider.getAgentById('personal_assistant'); |
|||
return agent?.name ?? 'Deep Voice'; |
|||
} |
|||
|
|||
Future<void> init() async { |
|||
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); |
|||
const iosSettings = DarwinInitializationSettings( |
|||
requestAlertPermission: false, // 初始化时不请求权限 |
|||
requestBadgePermission: false, |
|||
requestSoundPermission: false, |
|||
); |
|||
|
|||
const initSettings = InitializationSettings( |
|||
android: androidSettings, |
|||
iOS: iosSettings, |
|||
); |
|||
|
|||
await _notificationsPlugin.initialize( |
|||
initSettings, |
|||
onDidReceiveNotificationResponse: (details) { |
|||
if (Get.currentRoute != '/chat') { |
|||
Get.toNamed('/chat'); |
|||
} |
|||
}, |
|||
); |
|||
|
|||
// 创建通知渠道 |
|||
if (Platform.isAndroid) { |
|||
await _notificationsPlugin.resolvePlatformSpecificImplementation< |
|||
AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel( |
|||
const AndroidNotificationChannel( |
|||
'foreground_service', // id |
|||
'Foreground Service', // name |
|||
description: 'Used for the foreground service notification', // description |
|||
importance: Importance.min, |
|||
playSound: false, |
|||
enableVibration: false, |
|||
showBadge: false, |
|||
), |
|||
); |
|||
} |
|||
|
|||
await _requestPermissionIfNeeded(); |
|||
|
|||
// 在初始化完成后显示前台服务通知 |
|||
await showForegroundNotification(); |
|||
} |
|||
|
|||
Future<void> showForegroundNotification() async { |
|||
if (_notificationActive) return; |
|||
|
|||
// 在显示通知前确保有权限 |
|||
await _requestPermissionIfNeeded(); |
|||
|
|||
final androidDetails = AndroidNotificationDetails( |
|||
'foreground_service', |
|||
'Foreground Service', |
|||
channelDescription: 'Used for the foreground service notification', |
|||
importance: Importance.min, |
|||
priority: Priority.min, |
|||
ongoing: true, |
|||
autoCancel: false, |
|||
showWhen: false, |
|||
enableVibration: false, |
|||
playSound: false, |
|||
icon: '@mipmap/ic_launcher', |
|||
category: AndroidNotificationCategory.service, |
|||
actions: [ |
|||
const AndroidNotificationAction( |
|||
'chat', |
|||
'开始对话', |
|||
showsUserInterface: true, |
|||
), |
|||
], |
|||
); |
|||
|
|||
const iosDetails = DarwinNotificationDetails( |
|||
presentAlert: false, |
|||
presentBadge: false, |
|||
presentSound: false, |
|||
interruptionLevel: InterruptionLevel.passive, |
|||
); |
|||
|
|||
final notificationDetails = NotificationDetails( |
|||
android: androidDetails, |
|||
iOS: iosDetails, |
|||
); |
|||
|
|||
await _notificationsPlugin.show( |
|||
0, |
|||
_defaultAgentName, |
|||
'点击开始对话', |
|||
notificationDetails, |
|||
); |
|||
|
|||
_notificationActive = true; |
|||
} |
|||
|
|||
Future<void> cancelNotification() async { |
|||
if (!_notificationActive) return; |
|||
await _notificationsPlugin.cancel(0); |
|||
_notificationActive = false; |
|||
} |
|||
|
|||
Future<void> _requestPermissionIfNeeded() async { |
|||
if (_isRequestingPermission) return; |
|||
_isRequestingPermission = true; |
|||
|
|||
try { |
|||
if (Platform.isAndroid) { |
|||
final androidInfo = await DeviceInfoPlugin().androidInfo; |
|||
if (androidInfo.version.sdkInt >= 33) { |
|||
final status = await Permission.notification.status; |
|||
if (status.isDenied) { |
|||
await Permission.notification.request(); |
|||
} |
|||
} |
|||
} else if (Platform.isIOS) { |
|||
await _notificationsPlugin |
|||
.resolvePlatformSpecificImplementation< |
|||
IOSFlutterLocalNotificationsPlugin>() |
|||
?.requestPermissions( |
|||
alert: true, |
|||
badge: true, |
|||
sound: true, |
|||
); |
|||
} |
|||
} finally { |
|||
_isRequestingPermission = false; |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +1,9 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/chat_controller.dart'; |
|||
|
|||
class ChatBinding extends Bindings { |
|||
class ChatBinding implements Bindings { |
|||
@override |
|||
void dependencies() { |
|||
// 获取传递的参数 |
|||
final arguments = Get.arguments; |
|||
if (arguments == null || !(arguments is Map<String, dynamic>)) { |
|||
print('Chat binding: 未提供有效的参数'); |
|||
Get.back(); |
|||
return; |
|||
} |
|||
|
|||
// 创建并注入控制器 |
|||
Get.put(ChatController( |
|||
agentId: arguments['agentId'], |
|||
agentName: arguments['agentName'], |
|||
agentAvatar: arguments['agentAvatar'], |
|||
agentSubtitle: arguments['agentSubtitle'], |
|||
systemPrompt: arguments['systemPrompt'], |
|||
welcomeMessage: arguments['welcomeMessage'], |
|||
)); |
|||
Get.put(ChatController()); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue