|
|
@ -15,6 +15,7 @@ class Message { |
|
|
final bool isRecognizing; // 是否为语音识别中的临时消息 |
|
|
final bool isRecognizing; // 是否为语音识别中的临时消息 |
|
|
final bool hasImage; // 是否包含图片 |
|
|
final bool hasImage; // 是否包含图片 |
|
|
final String? imagePath; // 图片路径 |
|
|
final String? imagePath; // 图片路径 |
|
|
|
|
|
final Map? meta; //卡片数据 |
|
|
|
|
|
|
|
|
Message({ |
|
|
Message({ |
|
|
required this.isUser, |
|
|
required this.isUser, |
|
|
@ -23,50 +24,51 @@ class Message { |
|
|
this.isRecognizing = false, |
|
|
this.isRecognizing = false, |
|
|
this.hasImage = false, |
|
|
this.hasImage = false, |
|
|
this.imagePath, |
|
|
this.imagePath, |
|
|
|
|
|
this.meta, |
|
|
}) : this.timestamp = timestamp ?? DateTime.now(); |
|
|
}) : this.timestamp = timestamp ?? DateTime.now(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class AgentController extends GetxController { |
|
|
class AgentController extends GetxController { |
|
|
static AgentController get to => Get.find(); |
|
|
static AgentController get to => Get.find(); |
|
|
|
|
|
|
|
|
final logger = Logger(); |
|
|
final logger = Logger(); |
|
|
|
|
|
|
|
|
// 聊天消息列表 |
|
|
// 聊天消息列表 |
|
|
final messages = <Message>[].obs; |
|
|
final messages = <Message>[].obs; |
|
|
|
|
|
|
|
|
// 控制器 |
|
|
// 控制器 |
|
|
final TextEditingController textController = TextEditingController(); |
|
|
final TextEditingController textController = TextEditingController(); |
|
|
final ScrollController scrollController = ScrollController(); |
|
|
final ScrollController scrollController = ScrollController(); |
|
|
|
|
|
|
|
|
// 状态标志 |
|
|
// 状态标志 |
|
|
final isInitialized = false.obs; |
|
|
final isInitialized = false.obs; |
|
|
final isListening = false.obs; |
|
|
final isListening = false.obs; |
|
|
final isSpeaking = false.obs; |
|
|
final isSpeaking = false.obs; |
|
|
final isProcessing = false.obs; |
|
|
final isProcessing = false.obs; |
|
|
|
|
|
|
|
|
// 图片处理状态 |
|
|
// 图片处理状态 |
|
|
final isImageProcessing = false.obs; |
|
|
final isImageProcessing = false.obs; |
|
|
|
|
|
|
|
|
// 输入模式控制 |
|
|
// 输入模式控制 |
|
|
final isTextInputMode = true.obs; |
|
|
final isTextInputMode = true.obs; |
|
|
|
|
|
|
|
|
// 当前输入的文本内容 |
|
|
// 当前输入的文本内容 |
|
|
final currentText = ''.obs; |
|
|
final currentText = ''.obs; |
|
|
|
|
|
|
|
|
// 图片输入相关 |
|
|
// 图片输入相关 |
|
|
final isImageInputActive = false.obs; // 是否处于图片输入模式 |
|
|
final isImageInputActive = false.obs; // 是否处于图片输入模式 |
|
|
final selectedImagePath = Rx<String?>(null); // 当前选择的图片路径 |
|
|
final selectedImagePath = Rx<String?>(null); // 当前选择的图片路径 |
|
|
|
|
|
|
|
|
// 流事件订阅 |
|
|
// 流事件订阅 |
|
|
StreamSubscription<AgentServiceEvent>? _eventSubscription; |
|
|
StreamSubscription<AgentServiceEvent>? _eventSubscription; |
|
|
|
|
|
|
|
|
// 用于跟踪AI回复状态 |
|
|
// 用于跟踪AI回复状态 |
|
|
bool _isNewAssistantResponse = true; |
|
|
bool _isNewAssistantResponse = true; |
|
|
String _currentResponseId = ''; |
|
|
String _currentResponseId = ''; |
|
|
|
|
|
|
|
|
// 会话ID |
|
|
// 会话ID |
|
|
final String sessionId = 'default_agent'; |
|
|
final String sessionId = 'default_agent'; |
|
|
|
|
|
|
|
|
// 是否已加载聊天记录 |
|
|
// 是否已加载聊天记录 |
|
|
final isHistoryLoaded = false.obs; |
|
|
final isHistoryLoaded = false.obs; |
|
|
|
|
|
|
|
|
@ -86,7 +88,7 @@ class AgentController extends GetxController { |
|
|
_eventSubscription?.cancel(); |
|
|
_eventSubscription?.cancel(); |
|
|
super.onClose(); |
|
|
super.onClose(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 加载聊天历史记录 |
|
|
// 加载聊天历史记录 |
|
|
Future<void> _loadChatHistory() async { |
|
|
Future<void> _loadChatHistory() async { |
|
|
try { |
|
|
try { |
|
|
@ -96,19 +98,21 @@ class AgentController extends GetxController { |
|
|
page: 1, |
|
|
page: 1, |
|
|
pageSize: 20, |
|
|
pageSize: 20, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (result.messages.isNotEmpty) { |
|
|
if (result.messages.isNotEmpty) { |
|
|
// 转换并添加消息到列表 |
|
|
// 转换并添加消息到列表 |
|
|
final historyMessages = result.messages.map((msg) => Message( |
|
|
final historyMessages = result.messages |
|
|
isUser: msg.sender == 'user', |
|
|
.map((msg) => Message( |
|
|
text: msg.message, |
|
|
isUser: msg.sender == 'user', |
|
|
timestamp: DateTime.parse(msg.timestamp), |
|
|
text: msg.message, |
|
|
)).toList(); |
|
|
timestamp: DateTime.parse(msg.timestamp), |
|
|
|
|
|
)) |
|
|
|
|
|
.toList(); |
|
|
|
|
|
|
|
|
// 消息按时间排序,最新的消息在底部 |
|
|
// 消息按时间排序,最新的消息在底部 |
|
|
historyMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); |
|
|
historyMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); |
|
|
messages.assignAll(historyMessages); |
|
|
messages.assignAll(historyMessages); |
|
|
|
|
|
|
|
|
logger.i('成功加载 ${historyMessages.length} 条聊天记录'); |
|
|
logger.i('成功加载 ${historyMessages.length} 条聊天记录'); |
|
|
} else { |
|
|
} else { |
|
|
// 没有历史记录时添加欢迎消息 |
|
|
// 没有历史记录时添加欢迎消息 |
|
|
@ -120,16 +124,15 @@ class AgentController extends GetxController { |
|
|
_addWelcomeMessage(); |
|
|
_addWelcomeMessage(); |
|
|
} finally { |
|
|
} finally { |
|
|
isHistoryLoaded.value = true; |
|
|
isHistoryLoaded.value = true; |
|
|
|
|
|
|
|
|
messages.refresh(); |
|
|
messages.refresh(); |
|
|
// 在加载完成后直接定位到底部,无需动画 |
|
|
// 在加载完成后直接定位到底部,无需动画 |
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
scrollController.jumpTo(scrollController.position.maxScrollExtent); |
|
|
scrollController.jumpTo(scrollController.position.maxScrollExtent); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 添加欢迎消息 |
|
|
// 添加欢迎消息 |
|
|
void _addWelcomeMessage() { |
|
|
void _addWelcomeMessage() { |
|
|
messages.add(Message( |
|
|
messages.add(Message( |
|
|
@ -159,15 +162,16 @@ class AgentController extends GetxController { |
|
|
isTextInputMode.value = true; |
|
|
isTextInputMode.value = true; |
|
|
isTextInputMode.refresh(); |
|
|
isTextInputMode.refresh(); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.recognizing: |
|
|
case AgentServiceEventType.recognizing: |
|
|
final text = event.data['text'] ?? ''; |
|
|
final text = event.data['text'] ?? ''; |
|
|
currentText.value = text; // 保留当前文本,以便其他地方使用 |
|
|
currentText.value = text; // 保留当前文本,以便其他地方使用 |
|
|
|
|
|
|
|
|
if (text.isNotEmpty) { |
|
|
if (text.isNotEmpty) { |
|
|
// 查找是否有正在识别中的消息 |
|
|
// 查找是否有正在识别中的消息 |
|
|
final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
final index = messages |
|
|
|
|
|
.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
|
|
|
|
|
|
if (index >= 0) { |
|
|
if (index >= 0) { |
|
|
// 更新已有的识别中消息 |
|
|
// 更新已有的识别中消息 |
|
|
messages[index] = Message( |
|
|
messages[index] = Message( |
|
|
@ -188,13 +192,14 @@ class AgentController extends GetxController { |
|
|
_scrollToBottom(); |
|
|
_scrollToBottom(); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.recognitionResult: |
|
|
case AgentServiceEventType.recognitionResult: |
|
|
final text = event.data['text'] ?? ''; |
|
|
final text = event.data['text'] ?? ''; |
|
|
|
|
|
|
|
|
// 查找是否有正在识别中的消息 |
|
|
// 查找是否有正在识别中的消息 |
|
|
final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
final index = |
|
|
|
|
|
messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
|
|
|
|
|
|
if (text.isNotEmpty) { |
|
|
if (text.isNotEmpty) { |
|
|
if (index >= 0) { |
|
|
if (index >= 0) { |
|
|
// 将识别中的消息转换为最终消息 |
|
|
// 将识别中的消息转换为最终消息 |
|
|
@ -208,7 +213,7 @@ class AgentController extends GetxController { |
|
|
// 如果没有找到识别中的消息,添加新消息 |
|
|
// 如果没有找到识别中的消息,添加新消息 |
|
|
messages.add(Message(isUser: true, text: text)); |
|
|
messages.add(Message(isUser: true, text: text)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
isProcessing.value = true; |
|
|
isProcessing.value = true; |
|
|
// 标记为新的AI回复 |
|
|
// 标记为新的AI回复 |
|
|
_isNewAssistantResponse = true; |
|
|
_isNewAssistantResponse = true; |
|
|
@ -220,11 +225,11 @@ class AgentController extends GetxController { |
|
|
messages.refresh(); |
|
|
messages.refresh(); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.ttsStarted: |
|
|
case AgentServiceEventType.ttsStarted: |
|
|
isSpeaking.value = true; |
|
|
isSpeaking.value = true; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.ttsCompleted: |
|
|
case AgentServiceEventType.ttsCompleted: |
|
|
case AgentServiceEventType.ttsStopped: |
|
|
case AgentServiceEventType.ttsStopped: |
|
|
case AgentServiceEventType.ttsCanceled: |
|
|
case AgentServiceEventType.ttsCanceled: |
|
|
@ -238,16 +243,17 @@ class AgentController extends GetxController { |
|
|
case AgentServiceEventType.imageReady: |
|
|
case AgentServiceEventType.imageReady: |
|
|
// 图片准备就绪,但AI还没有开始处理,保持processing状态 |
|
|
// 图片准备就绪,但AI还没有开始处理,保持processing状态 |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.assistantToken: |
|
|
case AgentServiceEventType.assistantToken: |
|
|
if (!isProcessing.value) isProcessing.value = true; |
|
|
if (!isProcessing.value) isProcessing.value = true; |
|
|
|
|
|
|
|
|
final token = event.data['token'] ?? ''; |
|
|
final token = event.data['token'] ?? ''; |
|
|
final responseId = event.data['responseId'] ?? ''; |
|
|
final responseId = event.data['responseId'] ?? ''; |
|
|
|
|
|
|
|
|
if (token.isNotEmpty) { |
|
|
if (token.isNotEmpty) { |
|
|
// 如果是新的回复或者响应ID改变,创建新消息 |
|
|
// 如果是新的回复或者响应ID改变,创建新消息 |
|
|
if (_isNewAssistantResponse || (_currentResponseId != responseId && responseId.isNotEmpty)) { |
|
|
if (_isNewAssistantResponse || |
|
|
|
|
|
(_currentResponseId != responseId && responseId.isNotEmpty)) { |
|
|
messages.add(Message(isUser: false, text: token)); |
|
|
messages.add(Message(isUser: false, text: token)); |
|
|
_isNewAssistantResponse = false; |
|
|
_isNewAssistantResponse = false; |
|
|
_currentResponseId = responseId; |
|
|
_currentResponseId = responseId; |
|
|
@ -267,35 +273,37 @@ class AgentController extends GetxController { |
|
|
_scrollToBottom(); |
|
|
_scrollToBottom(); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.assistantResponse: |
|
|
case AgentServiceEventType.assistantResponse: |
|
|
isProcessing.value = false; |
|
|
isProcessing.value = false; |
|
|
isImageProcessing.value = false; |
|
|
isImageProcessing.value = false; |
|
|
// 标记当前回复完成 |
|
|
// 标记当前回复完成 |
|
|
_isNewAssistantResponse = true; |
|
|
_isNewAssistantResponse = true; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.error: |
|
|
case AgentServiceEventType.error: |
|
|
isListening.value = false; |
|
|
isListening.value = false; |
|
|
isSpeaking.value = false; |
|
|
isSpeaking.value = false; |
|
|
isProcessing.value = false; |
|
|
isProcessing.value = false; |
|
|
isImageProcessing.value = false; |
|
|
isImageProcessing.value = false; |
|
|
|
|
|
|
|
|
// 移除临时的识别消息 |
|
|
// 移除临时的识别消息 |
|
|
final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
final index = |
|
|
|
|
|
messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
if (index >= 0) { |
|
|
if (index >= 0) { |
|
|
messages.removeAt(index); |
|
|
messages.removeAt(index); |
|
|
messages.refresh(); |
|
|
messages.refresh(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
logger.e('代理服务错误: ${event.data['message']}'); |
|
|
logger.e('代理服务错误: ${event.data['message']}'); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.autoStop: |
|
|
case AgentServiceEventType.autoStop: |
|
|
isListening.value = false; |
|
|
isListening.value = false; |
|
|
|
|
|
|
|
|
// 移除临时的识别消息 |
|
|
// 移除临时的识别消息 |
|
|
final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
final index = |
|
|
|
|
|
messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); |
|
|
if (index >= 0) { |
|
|
if (index >= 0) { |
|
|
// 只有当消息为空或文本太短时才移除 |
|
|
// 只有当消息为空或文本太短时才移除 |
|
|
if (messages[index].text.trim().length < 2) { |
|
|
if (messages[index].text.trim().length < 2) { |
|
|
@ -309,26 +317,51 @@ class AgentController extends GetxController { |
|
|
timestamp: messages[index].timestamp, |
|
|
timestamp: messages[index].timestamp, |
|
|
); |
|
|
); |
|
|
messages.refresh(); |
|
|
messages.refresh(); |
|
|
|
|
|
|
|
|
// 发送此消息到AI处理 |
|
|
// 发送此消息到AI处理 |
|
|
isProcessing.value = true; |
|
|
isProcessing.value = true; |
|
|
_isNewAssistantResponse = true; |
|
|
_isNewAssistantResponse = true; |
|
|
AgentService.processTextInput(messages[index].text, speakResponse: true); |
|
|
AgentService.processTextInput(messages[index].text, |
|
|
|
|
|
speakResponse: true); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.functionCall: |
|
|
case AgentServiceEventType.functionCall: |
|
|
// final functionName = event.data['name'] ?? ''; |
|
|
final functionName = event.data['name'] ?? ''; |
|
|
// final arguments = event.data['arguments'] ?? ''; |
|
|
final arguments = event.data['arguments'] ?? ''; |
|
|
// logger.i('mcp: $functionName, $arguments'); |
|
|
logger.i('Fultter 开始调用 mcp: $functionName, $arguments'); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case AgentServiceEventType.functionCallResult: |
|
|
case AgentServiceEventType.functionCallResult: |
|
|
|
|
|
final responseId = event.data['responseId'] ?? ''; |
|
|
final result = event.data['result'] ?? ''; |
|
|
final result = event.data['result'] ?? ''; |
|
|
final functionCall = event.data['function_call'] ?? ''; |
|
|
final functionCall = event.data['function_call'] ?? ''; |
|
|
|
|
|
if (!result.isBlank) { |
|
|
|
|
|
final meta = result["meta"] ?? {}; |
|
|
|
|
|
//不为空 |
|
|
|
|
|
// 如果是新的回复或者响应ID改变,创建新消息 |
|
|
|
|
|
if (_isNewAssistantResponse || |
|
|
|
|
|
(_currentResponseId != responseId && responseId.isNotEmpty)) { |
|
|
|
|
|
messages.add(Message(isUser: false, text: "", meta: meta)); |
|
|
|
|
|
_isNewAssistantResponse = false; |
|
|
|
|
|
_currentResponseId = responseId; |
|
|
|
|
|
} else { |
|
|
|
|
|
// 否则更新最后一条消息 |
|
|
|
|
|
final lastIndex = messages.length - 1; |
|
|
|
|
|
|
|
|
|
|
|
if (lastIndex >= 0 && !messages[lastIndex].isUser) { |
|
|
|
|
|
messages[lastIndex] = Message( |
|
|
|
|
|
isUser: false, |
|
|
|
|
|
text: messages[lastIndex].text, |
|
|
|
|
|
meta: meta, |
|
|
|
|
|
timestamp: messages[lastIndex].timestamp, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
logger.i('Fultter 结束调用 mcp: $result, $functionCall'); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
default: |
|
|
default: |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
@ -359,22 +392,22 @@ class AgentController extends GetxController { |
|
|
final text = textController.text.trim(); |
|
|
final text = textController.text.trim(); |
|
|
final imagePath = selectedImagePath.value!; // 使用!强制断言非空 |
|
|
final imagePath = selectedImagePath.value!; // 使用!强制断言非空 |
|
|
await sendImageMessage(imagePath, text: text); |
|
|
await sendImageMessage(imagePath, text: text); |
|
|
|
|
|
|
|
|
// 清除图片输入状态 |
|
|
// 清除图片输入状态 |
|
|
clearImageInput(); |
|
|
clearImageInput(); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
final text = textController.text.trim(); |
|
|
final text = textController.text.trim(); |
|
|
if (text.isEmpty) return; |
|
|
if (text.isEmpty) return; |
|
|
|
|
|
|
|
|
final message = Message(isUser: true, text: text); |
|
|
final message = Message(isUser: true, text: text); |
|
|
messages.add(message); |
|
|
messages.add(message); |
|
|
textController.clear(); |
|
|
textController.clear(); |
|
|
|
|
|
|
|
|
// 滚动到底部 |
|
|
// 滚动到底部 |
|
|
_scrollToBottom(); |
|
|
_scrollToBottom(); |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
isProcessing.value = true; |
|
|
isProcessing.value = true; |
|
|
// 标记为新的AI回复 |
|
|
// 标记为新的AI回复 |
|
|
@ -392,31 +425,31 @@ class AgentController extends GetxController { |
|
|
logger.e('图片不存在: $imagePath'); |
|
|
logger.e('图片不存在: $imagePath'); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 显示的文本,如果没有提供则使用默认值 |
|
|
// 显示的文本,如果没有提供则使用默认值 |
|
|
final displayText = text?.isNotEmpty == true ? text! : '[图片]'; |
|
|
final displayText = text?.isNotEmpty == true ? text! : '[图片]'; |
|
|
|
|
|
|
|
|
// 添加用户消息 |
|
|
// 添加用户消息 |
|
|
final message = Message( |
|
|
final message = Message( |
|
|
isUser: true, |
|
|
isUser: true, |
|
|
text: displayText, |
|
|
text: displayText, |
|
|
hasImage: true, |
|
|
hasImage: true, |
|
|
imagePath: imagePath, |
|
|
imagePath: imagePath, |
|
|
); |
|
|
); |
|
|
messages.add(message); |
|
|
messages.add(message); |
|
|
|
|
|
|
|
|
// 清空文本输入 |
|
|
// 清空文本输入 |
|
|
textController.clear(); |
|
|
textController.clear(); |
|
|
|
|
|
|
|
|
// 滚动到底部 |
|
|
// 滚动到底部 |
|
|
_scrollToBottom(); |
|
|
_scrollToBottom(); |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
isProcessing.value = true; |
|
|
isProcessing.value = true; |
|
|
isImageProcessing.value = true; |
|
|
isImageProcessing.value = true; |
|
|
// 标记为新的AI回复 |
|
|
// 标记为新的AI回复 |
|
|
_isNewAssistantResponse = true; |
|
|
_isNewAssistantResponse = true; |
|
|
|
|
|
|
|
|
// 调用Agent Service处理图片 |
|
|
// 调用Agent Service处理图片 |
|
|
await AgentService.processImageInput( |
|
|
await AgentService.processImageInput( |
|
|
imagePath, |
|
|
imagePath, |
|
|
@ -433,7 +466,7 @@ class AgentController extends GetxController { |
|
|
// 开始语音输入 |
|
|
// 开始语音输入 |
|
|
Future<void> startVoiceInput() async { |
|
|
Future<void> startVoiceInput() async { |
|
|
if (isListening.value) return; |
|
|
if (isListening.value) return; |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
await AgentService.startConversation(); |
|
|
await AgentService.startConversation(); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
@ -444,7 +477,7 @@ class AgentController extends GetxController { |
|
|
// 停止语音输入 |
|
|
// 停止语音输入 |
|
|
Future<void> stopVoiceInput() async { |
|
|
Future<void> stopVoiceInput() async { |
|
|
if (!isListening.value) return; |
|
|
if (!isListening.value) return; |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
await AgentService.stopConversation(); |
|
|
await AgentService.stopConversation(); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
@ -456,21 +489,20 @@ class AgentController extends GetxController { |
|
|
Future<void> clearChat() async { |
|
|
Future<void> clearChat() async { |
|
|
try { |
|
|
try { |
|
|
await AgentService.clearChatHistory(); |
|
|
await AgentService.clearChatHistory(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 清空本地消息列表 |
|
|
// 清空本地消息列表 |
|
|
messages.clear(); |
|
|
messages.clear(); |
|
|
|
|
|
|
|
|
// 添加欢迎消息 |
|
|
// 添加欢迎消息 |
|
|
_addWelcomeMessage(); |
|
|
_addWelcomeMessage(); |
|
|
|
|
|
|
|
|
// 重置回复状态 |
|
|
// 重置回复状态 |
|
|
_isNewAssistantResponse = true; |
|
|
_isNewAssistantResponse = true; |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
logger.e('清除聊天历史失败: $e'); |
|
|
logger.e('清除聊天历史失败: $e'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 切换输入模式 |
|
|
// 切换输入模式 |
|
|
void toggleInputMode() { |
|
|
void toggleInputMode() { |
|
|
// 如果当前在图片输入模式,先清除 |
|
|
// 如果当前在图片输入模式,先清除 |
|
|
@ -478,9 +510,9 @@ class AgentController extends GetxController { |
|
|
clearImageInput(); |
|
|
clearImageInput(); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
isTextInputMode.toggle(); |
|
|
isTextInputMode.toggle(); |
|
|
|
|
|
|
|
|
// 切换到语音模式时,直接开始语音输入 |
|
|
// 切换到语音模式时,直接开始语音输入 |
|
|
if (!isTextInputMode.value) { |
|
|
if (!isTextInputMode.value) { |
|
|
startVoiceInput(); |
|
|
startVoiceInput(); |
|
|
@ -498,10 +530,10 @@ class AgentController extends GetxController { |
|
|
final XFile? image = await picker.pickImage( |
|
|
final XFile? image = await picker.pickImage( |
|
|
source: ImageSource.gallery, |
|
|
source: ImageSource.gallery, |
|
|
imageQuality: 80, // 设置图片质量 |
|
|
imageQuality: 80, // 设置图片质量 |
|
|
maxWidth: 1024, // 限制最大宽度 |
|
|
maxWidth: 1024, // 限制最大宽度 |
|
|
maxHeight: 1024, // 限制最大高度 |
|
|
maxHeight: 1024, // 限制最大高度 |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (image != null) { |
|
|
if (image != null) { |
|
|
// 设置已选择的图片 |
|
|
// 设置已选择的图片 |
|
|
selectedImagePath.value = image.path; |
|
|
selectedImagePath.value = image.path; |
|
|
@ -520,10 +552,10 @@ class AgentController extends GetxController { |
|
|
final XFile? photo = await picker.pickImage( |
|
|
final XFile? photo = await picker.pickImage( |
|
|
source: ImageSource.camera, |
|
|
source: ImageSource.camera, |
|
|
imageQuality: 80, // 设置图片质量 |
|
|
imageQuality: 80, // 设置图片质量 |
|
|
maxWidth: 1024, // 限制最大宽度 |
|
|
maxWidth: 1024, // 限制最大宽度 |
|
|
maxHeight: 1024, // 限制最大高度 |
|
|
maxHeight: 1024, // 限制最大高度 |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (photo != null) { |
|
|
if (photo != null) { |
|
|
// 设置已选择的图片 |
|
|
// 设置已选择的图片 |
|
|
selectedImagePath.value = photo.path; |
|
|
selectedImagePath.value = photo.path; |
|
|
@ -534,18 +566,18 @@ class AgentController extends GetxController { |
|
|
logger.e('拍照失败: $e'); |
|
|
logger.e('拍照失败: $e'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 清除图片输入状态 |
|
|
// 清除图片输入状态 |
|
|
void clearImageInput() { |
|
|
void clearImageInput() { |
|
|
selectedImagePath.value = null; |
|
|
selectedImagePath.value = null; |
|
|
isImageInputActive.value = false; |
|
|
isImageInputActive.value = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 使用预设提示词处理图片 |
|
|
// 使用预设提示词处理图片 |
|
|
void useImagePrompt(String prompt) { |
|
|
void useImagePrompt(String prompt) { |
|
|
// 确保selectedImagePath.value不为null |
|
|
// 确保selectedImagePath.value不为null |
|
|
if (selectedImagePath.value == null) return; |
|
|
if (selectedImagePath.value == null) return; |
|
|
|
|
|
|
|
|
// 填充文本框同时直接发送消息 |
|
|
// 填充文本框同时直接发送消息 |
|
|
textController.text = prompt; |
|
|
textController.text = prompt; |
|
|
// 立即发送图片和提示词 |
|
|
// 立即发送图片和提示词 |
|
|
@ -554,19 +586,19 @@ class AgentController extends GetxController { |
|
|
// 发送后清除图片输入状态 |
|
|
// 发送后清除图片输入状态 |
|
|
clearImageInput(); |
|
|
clearImageInput(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 预设提示词:总结图片内容 |
|
|
// 预设提示词:总结图片内容 |
|
|
void summarizeImage() { |
|
|
void summarizeImage() { |
|
|
useImagePrompt('请总结这张图片的内容'); |
|
|
useImagePrompt('请总结这张图片的内容'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 预设提示词:抽取图像文字 |
|
|
// 预设提示词:抽取图像文字 |
|
|
void extractTextFromImage() { |
|
|
void extractTextFromImage() { |
|
|
useImagePrompt('请提取这张图片中的所有文字'); |
|
|
useImagePrompt('请提取这张图片中的所有文字'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 预设提示词:翻译图像文字 |
|
|
// 预设提示词:翻译图像文字 |
|
|
void translateImageText() { |
|
|
void translateImageText() { |
|
|
useImagePrompt('请翻译这张图片中的文字'); |
|
|
useImagePrompt('请翻译这张图片中的文字'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|