bug fix:统一时间戳逻辑

This commit is contained in:
xinyan 2025-12-09 17:40:37 +08:00
parent 569e83fcbf
commit 9a7764c5d7

View file

@ -765,67 +765,102 @@ const fragmentsToContent = (fragments: any) => {
.join('\n\n'); .join('\n\n');
}; };
const parseDeepseekTimestamp = (value: any): number | null => {
// DeepSeek exports often use ISO strings; convert to Unix seconds
if (value === undefined || value === null) return null;
if (typeof value === 'number') return value;
const parsed = Date.parse(value);
if (Number.isNaN(parsed)) return null;
return Math.floor(parsed / 1000);
};
const convertDeepseekMessages = (convo) => { const convertDeepseekMessages = (convo) => {
// Parse DeepSeek chat messages (mapping + fragments) into chat dictionary
const mapping = convo['mapping']; const mapping = convo['mapping'];
const messages = []; const messages = [];
let currentId = ''; // 即使缺少这个函数,这里给一个简单的补丁以防报错
let lastId = null; const fragmentsToContent = (frags) => frags?.map(f => f.content).join('') || '';
const createdAt = parseDeepseekTimestamp(convo['create_time'] || convo['created_at']);
const updatedAt = parseDeepseekTimestamp(
convo['updated_at'] || convo['inserted_at'] || convo['create_time']
);
for (const message_id in mapping) { // 1. 先把所有节点处理一遍,存入字典,不管顺序
const message = mapping[message_id]; const rawNodes = {};
currentId = message_id;
try {
const fragments = message?.message?.fragments;
const content = fragmentsToContent(fragments);
if (messages.length === 0 && (!content || content === '')) { Object.keys(mapping).forEach(id => {
continue; const node = mapping[id];
} const fragments = node?.message?.fragments;
const content = fragments ? fragmentsToContent(fragments) : '';
const inferredRole = (() => { // 推断角色
let inferredRole = 'user';
if (Array.isArray(fragments)) { if (Array.isArray(fragments)) {
const firstType = fragments.find((f) => typeof f?.type === 'string')?.type; const firstType = fragments.find((f) => typeof f?.type === 'string')?.type;
if (firstType === 'REQUEST') return 'user'; if (firstType === 'RESPONSE') inferredRole = 'assistant';
if (firstType === 'RESPONSE') return 'assistant'; } else if (node?.message?.author?.role !== 'user') {
inferredRole = 'assistant';
} }
return message?.message?.author?.role !== 'user' ? 'assistant' : 'user';
})();
const new_chat = { rawNodes[id] = {
id: message_id, id: id,
parentId: lastId, originalParent: node.parent, // 保留原始 parent
childrenIds: message['children'] || [], childrenIds: node.children || [],
role: inferredRole, role: inferredRole,
content, content: content,
model: message?.message?.model || 'deepseek-chat', model: node?.message?.model || 'deepseek-chat',
hasContent: !!content && content.trim() !== ''
};
});
// 2. 按照父子关系重建顺序 (拓扑排序的简化版:顺藤摸瓜)
// 找到真正的 Root (通常是 message 为 null 的那个)
let currentNodeId = Object.keys(rawNodes).find(id => !rawNodes[id].originalParent);
// 如果找不到 null parent尝试找 'root'
if (!currentNodeId && rawNodes['root']) currentNodeId = 'root';
let lastValidId = null; // 用于记录上一个有效的内容节点,作为 parentId
// 3. 链式遍历
while (currentNodeId) {
const node = rawNodes[currentNodeId];
if (!node) break;
// 只有当有内容时才加入 messages 列表
if (node.hasContent) {
messages.push({
id: node.id,
parentId: lastValidId, // 指向上一个有效节点,而不是原始的空 root
childrenIds: node.childrenIds,
role: node.role,
content: node.content,
model: node.model,
done: true, done: true,
context: null context: null
}; });
messages.push(new_chat); lastValidId = node.id; // 更新有效节点指针
lastId = currentId;
} catch (error) {
console.log('Error with DeepSeek message', message, '\nError:', error);
}
} }
const history: Record<PropertyKey, (typeof messages)[number]> = {}; // 移动到下一个节点 (这里假设是单线性对话,取第一个 child)
// DeepSeek 的对话通常是线性的,如果有分支,这里需要更复杂的树形处理
currentNodeId = node.childrenIds[0];
}
const history = {};
messages.forEach((obj) => (history[obj.id] = obj)); messages.forEach((obj) => (history[obj.id] = obj));
const chat = { return {
history: { history: {
currentId: currentId, currentId: lastValidId, // 最后的 ID
messages: history messages: history
}, },
models: [messages[0]?.model || 'deepseek-chat'], models: [messages[0]?.model || 'deepseek-chat'],
messages: messages, messages: messages,
options: {}, options: {},
timestamp: convo['inserted_at'] || convo['updated_at'] || convo['create_time'], timestamp: updatedAt ?? createdAt ?? Math.floor(Date.now() / 1000),
title: convo['title'] ?? 'New Chat' title: convo['title'] ?? 'New Chat'
}; };
return chat;
}; };
const validateChat = (chat) => { const validateChat = (chat) => {
// Because ChatGPT sometimes has features we can't use like DALL-E or might have corrupted messages, need to validate // Because ChatGPT sometimes has features we can't use like DALL-E or might have corrupted messages, need to validate
const messages = chat.messages; const messages = chat.messages;
@ -886,6 +921,10 @@ export const convertDeepseekChats = (_chats) => {
for (const convo of _chats) { for (const convo of _chats) {
const chat = convertDeepseekMessages(convo); const chat = convertDeepseekMessages(convo);
const createdAt = parseDeepseekTimestamp(convo['create_time'] || convo['created_at']);
const updatedAt = parseDeepseekTimestamp(
convo['updated_at'] || convo['inserted_at'] || convo['create_time']
);
if (validateChat(chat)) { if (validateChat(chat)) {
chats.push({ chats.push({
@ -893,7 +932,9 @@ export const convertDeepseekChats = (_chats) => {
user_id: '', user_id: '',
title: convo['title'], title: convo['title'],
chat: chat, chat: chat,
timestamp: convo['inserted_at'] || convo['updated_at'] || convo['create_time'] timestamp: updatedAt ?? createdAt ?? null,
created_at: createdAt ?? null,
updated_at: updatedAt ?? null
}); });
} else { } else {
failed++; failed++;