Spaces:
Running
Running
import type { LayoutServerLoad } from "./$types"; | |
import { collections } from "$lib/server/database"; | |
import type { Conversation } from "$lib/types/Conversation"; | |
import { UrlDependency } from "$lib/types/UrlDependency"; | |
import { defaultModel, models, oldModels, validateModel } from "$lib/server/models"; | |
import { authCondition, requiresUser } from "$lib/server/auth"; | |
import { DEFAULT_SETTINGS } from "$lib/types/Settings"; | |
import { env } from "$env/dynamic/private"; | |
import { ObjectId } from "mongodb"; | |
import type { ConvSidebar } from "$lib/types/ConvSidebar"; | |
import { toolFromConfigs } from "$lib/server/tools"; | |
import { MetricsServer } from "$lib/server/metrics"; | |
import type { ToolFront, ToolInputFile } from "$lib/types/Tool"; | |
import { ReviewStatus } from "$lib/types/Review"; | |
import { base } from "$app/paths"; | |
export const load: LayoutServerLoad = async ({ locals, depends, fetch }) => { | |
depends(UrlDependency.ConversationList); | |
const settings = await collections.settings.findOne(authCondition(locals)); | |
// If the active model in settings is not valid, set it to the default model. This can happen if model was disabled. | |
if ( | |
settings && | |
!validateModel(models).safeParse(settings?.activeModel).success && | |
!settings.assistants?.map((el) => el.toString())?.includes(settings?.activeModel) | |
) { | |
settings.activeModel = defaultModel.id; | |
await collections.settings.updateOne(authCondition(locals), { | |
$set: { activeModel: defaultModel.id }, | |
}); | |
} | |
// if the model is unlisted, set the active model to the default model | |
if ( | |
settings?.activeModel && | |
models.find((m) => m.id === settings?.activeModel)?.unlisted === true | |
) { | |
settings.activeModel = defaultModel.id; | |
await collections.settings.updateOne(authCondition(locals), { | |
$set: { activeModel: defaultModel.id }, | |
}); | |
} | |
const enableAssistants = env.ENABLE_ASSISTANTS === "true"; | |
const assistantActive = !models.map(({ id }) => id).includes(settings?.activeModel ?? ""); | |
const assistant = assistantActive | |
? await collections.assistants.findOne({ | |
_id: new ObjectId(settings?.activeModel), | |
}) | |
: null; | |
const nConversations = await collections.conversations.countDocuments(authCondition(locals)); | |
const conversations = | |
nConversations === 0 | |
? Promise.resolve([]) | |
: fetch(`${base}/api/conversations`) | |
.then((res) => res.json()) | |
.then( | |
( | |
convs: Pick<Conversation, "_id" | "title" | "updatedAt" | "model" | "assistantId">[] | |
) => | |
convs.map((conv) => ({ | |
...conv, | |
updatedAt: new Date(conv.updatedAt), | |
})) | |
); | |
const userAssistants = settings?.assistants?.map((assistantId) => assistantId.toString()) ?? []; | |
const userAssistantsSet = new Set(userAssistants); | |
const assistants = conversations.then((conversations) => | |
collections.assistants | |
.find({ | |
_id: { | |
$in: [ | |
...userAssistants.map((el) => new ObjectId(el)), | |
...(conversations.map((conv) => conv.assistantId).filter((el) => !!el) as ObjectId[]), | |
], | |
}, | |
}) | |
.toArray() | |
); | |
const messagesBeforeLogin = env.MESSAGES_BEFORE_LOGIN ? parseInt(env.MESSAGES_BEFORE_LOGIN) : 0; | |
let loginRequired = false; | |
if (requiresUser && !locals.user) { | |
if (messagesBeforeLogin === 0) { | |
loginRequired = true; | |
} else if (nConversations >= messagesBeforeLogin) { | |
loginRequired = true; | |
} else { | |
// get the number of messages where `from === "assistant"` across all conversations. | |
const totalMessages = | |
( | |
await collections.conversations | |
.aggregate([ | |
{ $match: { ...authCondition(locals), "messages.from": "assistant" } }, | |
{ $project: { messages: 1 } }, | |
{ $limit: messagesBeforeLogin + 1 }, | |
{ $unwind: "$messages" }, | |
{ $match: { "messages.from": "assistant" } }, | |
{ $count: "messages" }, | |
]) | |
.toArray() | |
)[0]?.messages ?? 0; | |
loginRequired = totalMessages >= messagesBeforeLogin; | |
} | |
} | |
const toolUseDuration = (await MetricsServer.getMetrics().tool.toolUseDuration.get()).values; | |
const configToolIds = toolFromConfigs.map((el) => el._id.toString()); | |
let activeCommunityToolIds = (settings?.tools ?? []).filter( | |
(key) => !configToolIds.includes(key) | |
); | |
if (assistant) { | |
activeCommunityToolIds = [...activeCommunityToolIds, ...(assistant.tools ?? [])]; | |
} | |
const communityTools = await collections.tools | |
.find({ _id: { $in: activeCommunityToolIds.map((el) => new ObjectId(el)) } }) | |
.toArray() | |
.then((tools) => | |
tools.map((tool) => ({ | |
...tool, | |
isHidden: false, | |
isOnByDefault: true, | |
isLocked: true, | |
})) | |
); | |
return { | |
nConversations, | |
conversations: await conversations.then( | |
async (convs) => | |
await Promise.all( | |
convs.map(async (conv) => { | |
if (settings?.hideEmojiOnSidebar) { | |
conv.title = conv.title.replace(/\p{Emoji}/gu, ""); | |
} | |
// remove invalid unicode and trim whitespaces | |
conv.title = conv.title.replace(/\uFFFD/gu, "").trimStart(); | |
let avatarUrl: string | undefined = undefined; | |
if (conv.assistantId) { | |
const hash = ( | |
await collections.assistants.findOne({ | |
_id: new ObjectId(conv.assistantId), | |
}) | |
)?.avatar; | |
if (hash) { | |
avatarUrl = `/settings/assistants/${conv.assistantId}/avatar.jpg?hash=${hash}`; | |
} | |
} | |
return { | |
id: conv._id.toString(), | |
title: conv.title, | |
model: conv.model ?? defaultModel, | |
updatedAt: conv.updatedAt, | |
assistantId: conv.assistantId?.toString(), | |
avatarUrl, | |
} satisfies ConvSidebar; | |
}) | |
) | |
), | |
settings: { | |
searchEnabled: !!( | |
env.SERPAPI_KEY || | |
env.SERPER_API_KEY || | |
env.SERPSTACK_API_KEY || | |
env.SEARCHAPI_KEY || | |
env.YDC_API_KEY || | |
env.USE_LOCAL_WEBSEARCH || | |
env.SEARXNG_QUERY_URL || | |
env.BING_SUBSCRIPTION_KEY | |
), | |
ethicsModalAccepted: !!settings?.ethicsModalAcceptedAt, | |
ethicsModalAcceptedAt: settings?.ethicsModalAcceptedAt ?? null, | |
activeModel: settings?.activeModel ?? DEFAULT_SETTINGS.activeModel, | |
hideEmojiOnSidebar: settings?.hideEmojiOnSidebar ?? false, | |
shareConversationsWithModelAuthors: | |
settings?.shareConversationsWithModelAuthors ?? | |
DEFAULT_SETTINGS.shareConversationsWithModelAuthors, | |
customPrompts: settings?.customPrompts ?? {}, | |
assistants: userAssistants, | |
tools: | |
settings?.tools ?? | |
toolFromConfigs | |
.filter((el) => !el.isHidden && el.isOnByDefault) | |
.map((el) => el._id.toString()), | |
disableStream: settings?.disableStream ?? DEFAULT_SETTINGS.disableStream, | |
directPaste: settings?.directPaste ?? DEFAULT_SETTINGS.directPaste, | |
}, | |
models: models.map((model) => ({ | |
id: model.id, | |
name: model.name, | |
websiteUrl: model.websiteUrl, | |
modelUrl: model.modelUrl, | |
tokenizer: model.tokenizer, | |
datasetName: model.datasetName, | |
datasetUrl: model.datasetUrl, | |
displayName: model.displayName, | |
description: model.description, | |
reasoning: !!model.reasoning, | |
logoUrl: model.logoUrl, | |
promptExamples: model.promptExamples, | |
parameters: model.parameters, | |
preprompt: model.preprompt, | |
multimodal: model.multimodal, | |
multimodalAcceptedMimetypes: model.multimodalAcceptedMimetypes, | |
tools: model.tools, | |
unlisted: model.unlisted, | |
hasInferenceAPI: model.hasInferenceAPI, | |
})), | |
oldModels, | |
tools: [...toolFromConfigs, ...communityTools] | |
.filter((tool) => !tool?.isHidden) | |
.map( | |
(tool) => | |
({ | |
_id: tool._id.toString(), | |
type: tool.type, | |
displayName: tool.displayName, | |
name: tool.name, | |
description: tool.description, | |
mimeTypes: (tool.inputs ?? []) | |
.filter((input): input is ToolInputFile => input.type === "file") | |
.map((input) => (input as ToolInputFile).mimeTypes) | |
.flat(), | |
isOnByDefault: tool.isOnByDefault ?? true, | |
isLocked: tool.isLocked ?? true, | |
timeToUseMS: | |
toolUseDuration.find( | |
(el) => el.labels.tool === tool._id.toString() && el.labels.quantile === 0.9 | |
)?.value ?? 15_000, | |
color: tool.color, | |
icon: tool.icon, | |
}) satisfies ToolFront | |
), | |
communityToolCount: await collections.tools.countDocuments({ | |
type: "community", | |
review: ReviewStatus.APPROVED, | |
}), | |
assistants: assistants.then((assistants) => | |
assistants | |
.filter((el) => userAssistantsSet.has(el._id.toString())) | |
.map((el) => ({ | |
...el, | |
_id: el._id.toString(), | |
createdById: undefined, | |
createdByMe: | |
el.createdById.toString() === (locals.user?._id ?? locals.sessionId).toString(), | |
})) | |
), | |
user: locals.user && { | |
id: locals.user._id.toString(), | |
username: locals.user.username, | |
avatarUrl: locals.user.avatarUrl, | |
email: locals.user.email, | |
logoutDisabled: locals.user.logoutDisabled, | |
isAdmin: locals.user.isAdmin ?? false, | |
isEarlyAccess: locals.user.isEarlyAccess ?? false, | |
}, | |
assistant: assistant ? JSON.parse(JSON.stringify(assistant)) : null, | |
enableAssistants, | |
enableAssistantsRAG: env.ENABLE_ASSISTANTS_RAG === "true", | |
enableCommunityTools: env.COMMUNITY_TOOLS === "true", | |
loginRequired, | |
loginEnabled: requiresUser, | |
guestMode: requiresUser && messagesBeforeLogin > 0, | |
}; | |
}; | |