Files
Yi.Framework/Yi.Ai.Vue3/src/stores/modules/chat.ts
2025-12-14 18:55:46 +08:00

178 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ChatMessageVo } from '@/api/chat/types';
import { defineStore } from 'pinia';
import { getChatList } from '@/api';
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
import { useUserStore } from './user';
export const useChatStore = defineStore('chat', () => {
const userStore = useUserStore();
// 是否开启深度思考
const isDeepThinking = ref<boolean>(false);
const setDeepThinking = (value: boolean) => {
isDeepThinking.value = value;
};
// 会议ID对应-聊天记录 map对象
const chatMap = ref<Record<string, ChatMessageVo[]>>({});
/**
* 解析消息内容,提取文本、图片和文件信息
* @param content - 消息内容可能是字符串或数组格式的JSON字符串
* @returns 解析后的文本内容、图片列表和文件列表
*/
function parseMessageContent(content: string | any): {
text: string;
images: Array<{ url: string; name?: string }>;
files: Array<{ name: string; size: number }>;
} {
let text = '';
const images: Array<{ url: string; name?: string }> = [];
const files: Array<{ name: string; size: number }> = [];
try {
// 如果 content 是字符串,尝试解析为 JSON
let contentArray: any;
if (typeof content === 'string') {
// 尝试解析 JSON 数组格式
if (content.trim().startsWith('[')) {
contentArray = JSON.parse(content);
}
else {
// 普通文本
text = content;
return { text, images, files };
}
}
else {
contentArray = content;
}
// 如果不是数组,直接返回
if (!Array.isArray(contentArray)) {
text = String(content);
return { text, images, files };
}
// 遍历数组,提取文本和图片
for (const item of contentArray) {
if (item.type === 'text') {
text += item.text || '';
}
else if (item.type === 'image_url') {
if (item.image_url?.url) {
images.push({
url: item.image_url.url,
name: item.image_url.name,
});
}
}
}
// 从文本中提取文件信息(如果有 ATTACHMENT_FILE 标签)
const fileMatches = text.matchAll(/<ATTACHMENT_FILE>[\s\S]*?<FILE_NAME>(.*?)<\/FILE_NAME>[\s\S]*?<\/ATTACHMENT_FILE>/g);
for (const match of fileMatches) {
const fileName = match[1];
files.push({
name: fileName,
size: 0, // 从历史记录中无法获取文件大小
});
}
// 从文本中移除 ATTACHMENT_FILE 标签及其内容,只保留文件卡片显示
text = text.replace(/<ATTACHMENT_FILE>[\s\S]*?<\/ATTACHMENT_FILE>/g, '').trim();
return { text, images, files };
}
catch (error) {
console.error('解析消息内容失败:', error);
// 解析失败,返回原始内容
return {
text: String(content),
images: [],
files: [],
};
}
}
const setChatMap = (id: string, data: ChatMessageVo[]) => {
chatMap.value[id] = data?.map((item: ChatMessageVo) => {
const isUser = item.role === 'user';
// 解析消息内容
const { text, images, files } = parseMessageContent(item.content as string);
// 处理思考内容
const thinkContent = extractThkContent(text);
const finalContent = extractThkContentAfter(text);
return {
...item,
key: item.id,
placement: isUser ? 'end' : 'start',
isMarkdown: !isUser,
avatar: isUser
? getUserProfilePicture()
: systemProfilePicture,
avatarSize: '32px',
typing: false,
reasoning_content: thinkContent,
thinkingStatus: 'end',
content: finalContent,
thinlCollapse: false,
// 保留图片和文件信息(优先使用解析出来的,如果没有则使用原有的)
images: images.length > 0 ? images : item.images,
files: files.length > 0 ? files : item.files,
};
});
};
// 获取当前会话的聊天记录
const requestChatList = async (sessionId: string) => {
// 如果没有 token 则不查询聊天记录
if (!userStore.token)
return;
try {
const res = await getChatList({
sessionId,
userId: userStore.userInfo?.userId as number,
});
if (res.data.items) {
setChatMap(sessionId, res.data.items);
}
}
catch (error) {
console.error('getChatList:', error);
}
};
// 对思考中的内容回显做处理
function extractThkContent(content: string) {
const regex = /<think>(.*?)<\/think>/s;
const matchResult = content.match(regex);
// 把这些内容从 content 中移除
content = content.replace(regex, '');
return matchResult?.[1] ?? '';
}
// 如果有 </think> 标签,则把 </think> 之后的 内容从 content 中返回
function extractThkContentAfter(content: string) {
if (!content.includes('</think>')) {
return content;
}
const regex = /<\/think>(.*)/s;
const matchResult = content.match(regex);
// 把这些内容从 content 中移除
content = content.replace(regex, '');
return matchResult?.[1] ?? '';
}
return {
chatMap,
requestChatList,
isDeepThinking,
setDeepThinking,
};
});