270 lines
7.0 KiB
JavaScript
270 lines
7.0 KiB
JavaScript
import { defineStore } from 'pinia';
|
||
import apiService from '../services/api.js';
|
||
import agoraService from '../services/agora.js';
|
||
|
||
export const useChatStore = defineStore('chat', {
|
||
state: () => ({
|
||
messages: [],
|
||
isConnected: false,
|
||
isListening: false,
|
||
isSpeaking: false,
|
||
isProcessing: false,
|
||
currentTranscript: '',
|
||
error: null,
|
||
audioLevel: 0,
|
||
inConversation: false, // 是否在对话状态中
|
||
}),
|
||
|
||
getters: {
|
||
lastMessage: (state) => state.messages.length > 0 ? state.messages[state.messages.length - 1] : null,
|
||
isReady: (state) => state.isConnected && !state.error,
|
||
hasMessages: (state) => state.messages.length > 0,
|
||
},
|
||
|
||
actions: {
|
||
|
||
async initialize() {
|
||
try {
|
||
agoraService.init();
|
||
const response = await apiService.joinProject();
|
||
|
||
const agent_id = response.agent_id;
|
||
const create_ts =response.create_ts;
|
||
const status =response.status;
|
||
|
||
apiService.setAgentId(agent_id);
|
||
await agoraService.join(agent_id, create_ts, status)
|
||
|
||
this.isConnected = true;
|
||
this.error = null;
|
||
|
||
// Add greeting message
|
||
const greetingMessage = "你好呀,有什么可以帮您?";
|
||
if (greetingMessage) {
|
||
this.addMessage({
|
||
id: Date.now(),
|
||
content: greetingMessage,
|
||
sender: 'ai',
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
}
|
||
|
||
return true;
|
||
} catch (error) {
|
||
this.error = error.message || 'Failed to initialize chat';
|
||
console.error('Error initializing chat:', error);
|
||
return false;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 开始对话状态,启动持续监听
|
||
*/
|
||
startConversation() {
|
||
if (!this.isConnected) return false;
|
||
|
||
this.inConversation = true;
|
||
this.isListening = true;
|
||
this.currentTranscript = '';
|
||
|
||
// 设置语音段落回调和可读文本回调
|
||
agoraService.startConversation(
|
||
this.handleSpeechSegment.bind(this),
|
||
this.handleReadableText.bind(this)
|
||
);
|
||
|
||
// 启动音频发布
|
||
if (!agoraService.localAudioTrack) {
|
||
agoraService.startAudioPublishing();
|
||
}
|
||
return true;
|
||
},
|
||
|
||
/**
|
||
* 处理可读文本
|
||
*/
|
||
handleReadableText(text) {
|
||
if (!text || !text.trim()) return;
|
||
const isFromAI = text.includes('\n\n') || text.includes('<think>\n');
|
||
const currentTime = new Date();
|
||
const sender = isFromAI ? 'ai' : 'user';
|
||
|
||
// 获取最后一条消息
|
||
const lastMessage = this.lastMessage;
|
||
|
||
// 检查是否需要创建新消息或更新现有消息
|
||
if (lastMessage && lastMessage.sender === sender) {
|
||
// 计算时间差(毫秒)
|
||
const timeDiff = currentTime - new Date(lastMessage.timestamp);
|
||
|
||
// 如果时间差小于3秒,更新最后一条消息
|
||
if (timeDiff < 5000) {
|
||
// 更新最后一条消息的内容
|
||
lastMessage.content = text;
|
||
// 更新时间戳
|
||
lastMessage.timestamp = currentTime.toISOString();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 如果时间差大于3秒或者是不同发送者,添加新消息
|
||
this.addMessage({
|
||
id: Date.now(),
|
||
content: text,
|
||
sender: sender,
|
||
timestamp: currentTime.toISOString(),
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 结束对话状态,停止持续监听
|
||
*/
|
||
endConversation() {
|
||
this.inConversation = false;
|
||
this.isListening = false;
|
||
|
||
// 移除语音事件监听
|
||
// this.removeSpeechEventListeners();
|
||
|
||
// 结束Agora对话状态
|
||
agoraService.endConversation();
|
||
|
||
return true;
|
||
},
|
||
|
||
/**
|
||
* 处理语音更新事件
|
||
* @param {CustomEvent} event - 包含语音文本的事件
|
||
*/
|
||
handleSpeechUpdate(event) {
|
||
if (event.detail && event.detail.text) {
|
||
this.updateTranscript(event.detail.text);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 处理语音段落
|
||
* @param {string} text - 语音段落文本
|
||
*/
|
||
handleSpeechSegment(text) {
|
||
console.log("处理语音段落:", text);
|
||
if (!text || !text.trim()) return;
|
||
|
||
// 更新当前转写
|
||
this.updateTranscript(text);
|
||
|
||
// 使用与handleReadableText相同的逻辑处理消息
|
||
const currentTime = new Date();
|
||
const sender = 'user'; // 语音段落总是来自用户
|
||
|
||
// 获取最后一条消息
|
||
const lastMessage = this.lastMessage;
|
||
|
||
// 检查是否需要创建新消息或更新现有消息
|
||
if (lastMessage && lastMessage.sender === sender) {
|
||
// 计算时间差(毫秒)
|
||
const timeDiff = currentTime - new Date(lastMessage.timestamp);
|
||
|
||
// 如果时间差小于3秒,更新最后一条消息
|
||
if (timeDiff < 3000) {
|
||
// 更新最后一条消息的内容
|
||
lastMessage.content = text;
|
||
// 更新时间戳
|
||
lastMessage.timestamp = currentTime.toISOString();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 如果时间差大于3秒或者是不同发送者,添加新消息
|
||
this.addMessage({
|
||
id: Date.now(),
|
||
content: text,
|
||
sender: sender,
|
||
timestamp: currentTime.toISOString(),
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 开始监听(兼容旧接口)
|
||
*/
|
||
startListening() {
|
||
if (this.inConversation) return;
|
||
this.startConversation();
|
||
},
|
||
|
||
/**
|
||
* 停止监听(兼容旧接口)
|
||
*/
|
||
stopListening() {
|
||
if (!this.inConversation) return;
|
||
|
||
// 如果有转写内容,使用handleSpeechSegment处理消息
|
||
if (this.currentTranscript.trim()) {
|
||
this.handleSpeechSegment(this.currentTranscript.trim());
|
||
}
|
||
|
||
this.endConversation();
|
||
},
|
||
|
||
/**
|
||
* Update the current transcript
|
||
* @param {string} transcript - The current transcript
|
||
*/
|
||
updateTranscript(transcript) {
|
||
this.currentTranscript = transcript;
|
||
},
|
||
|
||
/**
|
||
* Add a message to the list
|
||
*/
|
||
addMessage(message) {
|
||
this.messages.push(message);
|
||
},
|
||
|
||
/**
|
||
* Clear all messages
|
||
*/
|
||
clearMessages() {
|
||
this.messages = [];
|
||
},
|
||
|
||
/**
|
||
* Update the audio level
|
||
*/
|
||
updateAudioLevel(level) {
|
||
this.audioLevel = level;
|
||
},
|
||
|
||
/**
|
||
* End the chat session
|
||
*/
|
||
async endSession() {
|
||
try {
|
||
// 如果在对话状态中,先结束对话
|
||
if (this.inConversation) {
|
||
this.endConversation();
|
||
}
|
||
|
||
// Leave the Agora channel
|
||
await agoraService.leave();
|
||
|
||
// End the API session
|
||
await apiService.endSession();
|
||
|
||
this.isConnected = false;
|
||
this.isListening = false;
|
||
this.isProcessing = false;
|
||
this.currentTranscript = '';
|
||
this.inConversation = false;
|
||
this.error = null;
|
||
|
||
return true;
|
||
} catch (error) {
|
||
this.error = error.message || 'Failed to end session';
|
||
console.error('Error ending session:', error);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
});
|