fix: copy tool calls, generations, and timestamps when forking topics
Forked topics dropped toolCall/generation relations and rewrote part lastUpdatedAt, which broke tool rendering, token stats, and reasoning durations.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import * as z from 'zod';
|
||||
import { messages, messageParts, attachments, topics } from '~~/drizzle/schema';
|
||||
import { messages, messageParts, attachments, topics, generations, toolCalls } from '~~/drizzle/schema';
|
||||
import { db } from '~~/server/lib/db';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { eq, and, inArray } from 'drizzle-orm';
|
||||
@@ -132,6 +132,38 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
|
||||
const messageIdMap = new Map<string, string>();
|
||||
const generationIdMap = new Map<string, string>();
|
||||
const toolCallIdMap = new Map<string, string>();
|
||||
|
||||
const originalMessageIds = messagesToCopy.map(m => m.id);
|
||||
const originalGenerationIds = [
|
||||
...new Set(
|
||||
messagesToCopy
|
||||
.map(m => m.generationId)
|
||||
.filter((id): id is string => id !== null),
|
||||
),
|
||||
];
|
||||
|
||||
// Copy generations first so messages can reference them
|
||||
if (originalGenerationIds.length > 0) {
|
||||
const generationsToCopy = await db.select().from(generations)
|
||||
.where(inArray(generations.id, originalGenerationIds));
|
||||
|
||||
for (const generation of generationsToCopy) {
|
||||
const newGenerationId = nanoid();
|
||||
generationIdMap.set(generation.id, newGenerationId);
|
||||
|
||||
await db.insert(generations).values({
|
||||
id: newGenerationId,
|
||||
userId,
|
||||
topicId: newTopicId,
|
||||
modelId: generation.modelId,
|
||||
status: generation.status,
|
||||
tokens: generation.tokens,
|
||||
error: generation.error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const msg of messagesToCopy) {
|
||||
const newMessageId = nanoid();
|
||||
@@ -142,13 +174,13 @@ export default defineEventHandler(async (event) => {
|
||||
userId,
|
||||
topicId: newTopicId,
|
||||
parentMessageId: msg.parentMessageId ? (messageIdMap.get(msg.parentMessageId) ?? null) : null,
|
||||
generationId: null,
|
||||
generationId: msg.generationId ? (generationIdMap.get(msg.generationId) ?? null) : null,
|
||||
role: msg.role,
|
||||
content: msg.content,
|
||||
activeChildId: null,
|
||||
deleted: msg.deleted,
|
||||
createdAt: msg.createdAt,
|
||||
updatedAt: new Date(),
|
||||
updatedAt: msg.updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,11 +196,40 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
|
||||
const originalMessageIds = Array.from(messageIdMap.keys());
|
||||
if (originalMessageIds.length > 0) {
|
||||
const parts = await db.select().from(messageParts)
|
||||
.where(inArray(messageParts.messageId, originalMessageIds));
|
||||
|
||||
// Copy referenced tool calls before parts so FKs resolve
|
||||
const originalToolCallIds = [
|
||||
...new Set(
|
||||
parts
|
||||
.map(p => p.toolCallId)
|
||||
.filter((id): id is string => id !== null),
|
||||
),
|
||||
];
|
||||
|
||||
if (originalToolCallIds.length > 0) {
|
||||
const toolCallsToCopy = await db.select().from(toolCalls)
|
||||
.where(inArray(toolCalls.id, originalToolCallIds));
|
||||
|
||||
for (const toolCall of toolCallsToCopy) {
|
||||
const newToolCallId = nanoid();
|
||||
toolCallIdMap.set(toolCall.id, newToolCallId);
|
||||
|
||||
await db.insert(toolCalls).values({
|
||||
id: newToolCallId,
|
||||
userId,
|
||||
toolName: toolCall.toolName,
|
||||
status: toolCall.status,
|
||||
input: toolCall.input,
|
||||
output: toolCall.output,
|
||||
error: toolCall.error,
|
||||
createdAt: toolCall.createdAt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const part of parts) {
|
||||
const newMessageId = messageIdMap.get(part.messageId);
|
||||
if (!newMessageId) continue;
|
||||
@@ -177,12 +238,14 @@ export default defineEventHandler(async (event) => {
|
||||
userId,
|
||||
topicId: newTopicId,
|
||||
messageId: newMessageId,
|
||||
toolCallId: part.toolCallId ? (toolCallIdMap.get(part.toolCallId) ?? null) : null,
|
||||
type: part.type,
|
||||
content: part.content,
|
||||
providerOptions: part.providerOptions,
|
||||
finished: part.finished,
|
||||
// Preserve original timestamps so reasoning duration stays accurate
|
||||
createdAt: part.createdAt,
|
||||
lastUpdatedAt: new Date(),
|
||||
lastUpdatedAt: part.lastUpdatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -198,7 +261,7 @@ export default defineEventHandler(async (event) => {
|
||||
topicId: newTopicId,
|
||||
messageId: newMessageId,
|
||||
fileId: attachment.fileId,
|
||||
createdAt: new Date(),
|
||||
createdAt: attachment.createdAt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user