bug fixes, and performance improvements, and drawer
This commit is contained in:
@@ -1,44 +1,39 @@
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import { topics } from '~~/drizzle/schema';
|
||||
import { db } from '~~/server/lib/db';
|
||||
import { cancelPendingRename } from '~~/server/utils/renames';
|
||||
import { userEvents } from '~~/server/utils/events';
|
||||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import { topics } from "~~/drizzle/schema";
|
||||
import { db } from "~~/server/lib/db";
|
||||
import { cancelPendingRename } from "~~/server/utils/renames";
|
||||
import { topicEvents, userEvents } from "~~/server/utils/events";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const topicId = getRouterParam(event, 'topicId')!;
|
||||
|
||||
const success = cancelPendingRename(topicId);
|
||||
if (success) {
|
||||
const res = await db.update(topics).set({
|
||||
renaming: false,
|
||||
}).where(and(eq(topics.id, topicId), eq(topics.userId, event.context.user!.id)));
|
||||
if (res.rowCount === 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Invalid topic',
|
||||
data: {
|
||||
code: 'INVALID_TOPIC',
|
||||
ok: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
userEvents.emit(topicId, 'topics', {
|
||||
op: 'update',
|
||||
payload: {
|
||||
topicId,
|
||||
renaming: false,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
};
|
||||
const topicId = getRouterParam(event, "topicId");
|
||||
const userId = event.context.user?.id;
|
||||
if (!topicId || !userId) {
|
||||
setResponseStatus(event, 400);
|
||||
return { ok: false };
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
};
|
||||
});
|
||||
const topic = await db.query.topics.findFirst({ where: { id: topicId, userId } });
|
||||
if (!topic) {
|
||||
setResponseStatus(event, 404);
|
||||
return { ok: false };
|
||||
}
|
||||
const cleared = await db.update(topics).set({
|
||||
renaming: false,
|
||||
renameAttemptId: null,
|
||||
renameExpiresAt: null,
|
||||
}).where(and(
|
||||
eq(topics.id, topicId),
|
||||
eq(topics.userId, userId),
|
||||
topic.renameAttemptId ? eq(topics.renameAttemptId, topic.renameAttemptId) : isNull(topics.renameAttemptId),
|
||||
)).returning({ id: topics.id });
|
||||
if (cleared.length === 0) {
|
||||
return { ok: false };
|
||||
}
|
||||
cancelPendingRename(topicId, topic.renameAttemptId);
|
||||
|
||||
const payload = { topicId, renaming: false };
|
||||
userEvents.emit(userId, "topics", { op: "update", payload });
|
||||
topicEvents.emit(topicId, { type: "topic_updated", payload });
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import * as z from 'zod';
|
||||
import { renamePrompt } from '~~/prompts';
|
||||
import { generateText } from 'ai';
|
||||
import { generateText, type UserContent } from "ai";
|
||||
import { nanoid } from "nanoid";
|
||||
import { resolveFileUrl } from "~~/utils/url";
|
||||
import { generateFileToken } from "~~/server/utils/file-token";
|
||||
import { GatewayFetchError, getProviderDetails, type ModelGateway } from '~~/server/utils/ai-provider';
|
||||
import { addPendingRename } from '~~/server/utils/renames';
|
||||
import { addPendingRename, completeRename } from "~~/server/utils/renames";
|
||||
import { userEvents } from '~~/server/utils/events';
|
||||
import { db } from '~~/server/lib/db';
|
||||
import { type Model } from '~/composables/useModels';
|
||||
import { topics } from '~~/drizzle/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { and, eq, gt, isNull, lte, or } from "drizzle-orm";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
@@ -94,7 +97,7 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
if (topic.renaming) {
|
||||
if (topic.renaming && topic.renameExpiresAt && topic.renameExpiresAt > new Date()) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Already renaming',
|
||||
@@ -165,47 +168,70 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
let prompt = firstMessage.content!;
|
||||
|
||||
if (firstMessage.attachments.length > 0) {
|
||||
for (const attachment of firstMessage.attachments) {
|
||||
if (attachment.file.mimeType.startsWith('image/')) {
|
||||
prompt += `\n`;
|
||||
const content: UserContent = [{
|
||||
type: "text",
|
||||
text: [firstMessage.content || "Describe the attached content.",
|
||||
...firstMessage.attachments.map(({ file }) => `Attachment: ${file.name}`),
|
||||
].join("\n").slice(0, 12000),
|
||||
}];
|
||||
if (model.inputModalities.includes("image")) {
|
||||
const images = firstMessage.attachments.filter(({ file }) =>
|
||||
file.mimeType.startsWith("image/") && file.size <= 10 * 1024 * 1024,
|
||||
).slice(0, 2);
|
||||
for (const { file } of images) {
|
||||
const url = new URL(resolveFileUrl(file.url));
|
||||
if (file.url.startsWith("/api/files/")) {
|
||||
const secret = process.env.BETTER_AUTH_SECRET;
|
||||
if (!secret) {
|
||||
continue;
|
||||
}
|
||||
const { exp, sig } = generateFileToken(file.url.slice("/api/files/".length), secret);
|
||||
url.searchParams.set("exp", String(exp));
|
||||
url.searchParams.set("sig", sig);
|
||||
}
|
||||
content.push({ type: "image", image: url, mediaType: file.mimeType });
|
||||
}
|
||||
}
|
||||
|
||||
const abortController = addPendingRename(topicId);
|
||||
event.waitUntil(autoRename(topicId, abortController, { gateway: gateway.gateway, model }, gateway.textTransformer, prompt, userId));
|
||||
const attemptId = nanoid();
|
||||
const claimed = await db.update(topics).set({
|
||||
renaming: true,
|
||||
renameAttemptId: attemptId,
|
||||
renameExpiresAt: new Date(Date.now() + 90000),
|
||||
}).where(and(
|
||||
eq(topics.id, topicId),
|
||||
eq(topics.userId, userId),
|
||||
eq(topics.name, topic.name),
|
||||
or(eq(topics.renaming, false), isNull(topics.renameExpiresAt), lte(topics.renameExpiresAt, new Date())),
|
||||
)).returning({ id: topics.id });
|
||||
if (claimed.length === 0) {
|
||||
return { ok: false as const, code: "ALREADY_RENAMING" };
|
||||
}
|
||||
userEvents.emit(userId, "topics", {
|
||||
op: "update", payload: { topicId, renaming: true },
|
||||
});
|
||||
const abortController = addPendingRename(topicId, attemptId);
|
||||
event.waitUntil(autoRename(topicId, attemptId, abortController, { gateway: gateway.gateway, model }, gateway.textTransformer, content, userId));
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
ok: true as const,
|
||||
};
|
||||
});
|
||||
|
||||
const autoRename = async (
|
||||
topicId: string,
|
||||
attemptId: string,
|
||||
abortController: AbortController,
|
||||
model: {
|
||||
gateway: ModelGateway,
|
||||
model: Model,
|
||||
},
|
||||
textTransformer: ((text: string) => string) | ((text: string) => string)[] | undefined,
|
||||
prompt: string,
|
||||
content: UserContent,
|
||||
userId: string,
|
||||
) => {
|
||||
const deadline = setTimeout(() => abortController.abort(), 90000);
|
||||
try {
|
||||
await db.update(topics).set({
|
||||
renaming: true,
|
||||
}).where(eq(topics.id, topicId));
|
||||
userEvents.emit(model.model.userId, 'topics', {
|
||||
op: 'update',
|
||||
payload: {
|
||||
topicId,
|
||||
renaming: true,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await generateText({
|
||||
model: model.gateway(model.model.externalId),
|
||||
providerOptions: {
|
||||
@@ -214,10 +240,11 @@ const autoRename = async (
|
||||
}
|
||||
},
|
||||
system: renamePrompt,
|
||||
prompt,
|
||||
messages: [{ role: "user", content }],
|
||||
maxRetries: 0,
|
||||
timeout: 90 * 1000,
|
||||
abortSignal: abortController.signal,
|
||||
})
|
||||
});
|
||||
|
||||
let text = response.text;
|
||||
|
||||
@@ -231,15 +258,30 @@ const autoRename = async (
|
||||
}
|
||||
}
|
||||
|
||||
await db.update(topics).set({
|
||||
text = text.trim().replace(/^['"]|['"]$/g, "").split("\n")[0]?.trim().slice(0, 120) || "";
|
||||
if (!text || abortController.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
const updated = await db.update(topics).set({
|
||||
renaming: false,
|
||||
renameAttemptId: null,
|
||||
renameExpiresAt: null,
|
||||
titleSource: "generated",
|
||||
name: text,
|
||||
}).where(eq(topics.id, topicId));
|
||||
}).where(and(
|
||||
eq(topics.id, topicId),
|
||||
eq(topics.renameAttemptId, attemptId),
|
||||
gt(topics.renameExpiresAt, new Date()),
|
||||
)).returning({ id: topics.id });
|
||||
if (updated.length === 0) {
|
||||
return;
|
||||
}
|
||||
userEvents.emit(model.model.userId, 'topics', {
|
||||
op: 'update',
|
||||
payload: {
|
||||
topicId,
|
||||
name: text,
|
||||
titleSource: "generated",
|
||||
renaming: false,
|
||||
},
|
||||
});
|
||||
@@ -248,29 +290,26 @@ const autoRename = async (
|
||||
payload: {
|
||||
topicId,
|
||||
name: text,
|
||||
titleSource: "generated",
|
||||
renaming: false,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to auto-rename:', error);
|
||||
await db.update(topics).set({
|
||||
renaming: false,
|
||||
}).where(eq(topics.id, topicId));
|
||||
userEvents.emit(model.model.userId, 'topics', {
|
||||
op: 'update',
|
||||
payload: {
|
||||
topicId,
|
||||
renaming: false,
|
||||
},
|
||||
});
|
||||
topicEvents.emit(topicId, {
|
||||
type: 'topic_updated',
|
||||
payload: {
|
||||
topicId,
|
||||
renaming: false,
|
||||
},
|
||||
});
|
||||
if (!abortController.signal.aborted) {
|
||||
console.error("Failed to auto-rename:", error);
|
||||
}
|
||||
} finally {
|
||||
completeRename(topicId);
|
||||
clearTimeout(deadline);
|
||||
completeRename(topicId, attemptId);
|
||||
const cleared = await db.update(topics).set({
|
||||
renaming: false,
|
||||
renameAttemptId: null,
|
||||
renameExpiresAt: null,
|
||||
}).where(and(eq(topics.id, topicId), eq(topics.renameAttemptId, attemptId))).returning({ id: topics.id });
|
||||
if (cleared.length > 0) {
|
||||
const payload = { topicId, renaming: false };
|
||||
userEvents.emit(userId, "topics", { op: "update", payload });
|
||||
topicEvents.emit(topicId, { type: "topic_updated", payload });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user