|
|
const prisma = require("../utils/prisma"); |
|
|
const moment = require("moment"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const SystemPromptVariables = { |
|
|
VALID_TYPES: ["user", "system", "static"], |
|
|
DEFAULT_VARIABLES: [ |
|
|
{ |
|
|
key: "time", |
|
|
value: () => moment().format("LTS"), |
|
|
description: "Current time", |
|
|
type: "system", |
|
|
multiUserRequired: false, |
|
|
}, |
|
|
{ |
|
|
key: "date", |
|
|
value: () => moment().format("LL"), |
|
|
description: "Current date", |
|
|
type: "system", |
|
|
multiUserRequired: false, |
|
|
}, |
|
|
{ |
|
|
key: "datetime", |
|
|
value: () => moment().format("LLLL"), |
|
|
description: "Current date and time", |
|
|
type: "system", |
|
|
multiUserRequired: false, |
|
|
}, |
|
|
{ |
|
|
key: "user.name", |
|
|
value: async (userId = null) => { |
|
|
if (!userId) return "[User name]"; |
|
|
try { |
|
|
const user = await prisma.users.findUnique({ |
|
|
where: { id: Number(userId) }, |
|
|
select: { username: true }, |
|
|
}); |
|
|
return user?.username || "[User name is empty or unknown]"; |
|
|
} catch (error) { |
|
|
console.error("Error fetching user name:", error); |
|
|
return "[User name is empty or unknown]"; |
|
|
} |
|
|
}, |
|
|
description: "Current user's username", |
|
|
type: "user", |
|
|
multiUserRequired: true, |
|
|
}, |
|
|
{ |
|
|
key: "user.bio", |
|
|
value: async (userId = null) => { |
|
|
if (!userId) return "[User bio]"; |
|
|
try { |
|
|
const user = await prisma.users.findUnique({ |
|
|
where: { id: Number(userId) }, |
|
|
select: { bio: true }, |
|
|
}); |
|
|
return user?.bio || "[User bio is empty]"; |
|
|
} catch (error) { |
|
|
console.error("Error fetching user bio:", error); |
|
|
return "[User bio is empty]"; |
|
|
} |
|
|
}, |
|
|
description: "Current user's bio field from their profile", |
|
|
type: "user", |
|
|
multiUserRequired: true, |
|
|
}, |
|
|
], |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get: async function (key = null) { |
|
|
if (!key) return null; |
|
|
const variable = await prisma.system_prompt_variables.findUnique({ |
|
|
where: { key: String(key) }, |
|
|
}); |
|
|
return variable; |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getAll: async function (userId = null) { |
|
|
|
|
|
const userDefinedSystemVariables = |
|
|
await prisma.system_prompt_variables.findMany(); |
|
|
const formattedDbVars = userDefinedSystemVariables.map((v) => ({ |
|
|
id: v.id, |
|
|
key: v.key, |
|
|
value: v.value, |
|
|
description: v.description, |
|
|
type: v.type, |
|
|
userId: v.userId, |
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
const defaultSystemVariables = !userId |
|
|
? this.DEFAULT_VARIABLES.filter((v) => !v.multiUserRequired) |
|
|
: this.DEFAULT_VARIABLES; |
|
|
|
|
|
return [...defaultSystemVariables, ...formattedDbVars]; |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
create: async function ({ |
|
|
key, |
|
|
value, |
|
|
description = null, |
|
|
type = "static", |
|
|
userId = null, |
|
|
}) { |
|
|
await this._checkVariableKey(key, true); |
|
|
return await prisma.system_prompt_variables.create({ |
|
|
data: { |
|
|
key: String(key), |
|
|
value: String(value), |
|
|
description: description ? String(description) : null, |
|
|
type: type ? String(type) : "static", |
|
|
userId: userId ? Number(userId) : null, |
|
|
}, |
|
|
}); |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
update: async function (id, { key, value, description = null }) { |
|
|
if (!id || !key || !value) return null; |
|
|
const existingRecord = await prisma.system_prompt_variables.findFirst({ |
|
|
where: { id: Number(id) }, |
|
|
}); |
|
|
if (!existingRecord) throw new Error("System prompt variable not found"); |
|
|
await this._checkVariableKey(key, false); |
|
|
|
|
|
return await prisma.system_prompt_variables.update({ |
|
|
where: { id: existingRecord.id }, |
|
|
data: { |
|
|
key: String(key), |
|
|
value: String(value), |
|
|
description: description ? String(description) : null, |
|
|
}, |
|
|
}); |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete: async function (id = null) { |
|
|
try { |
|
|
await prisma.system_prompt_variables.delete({ |
|
|
where: { id: Number(id) }, |
|
|
}); |
|
|
return true; |
|
|
} catch (error) { |
|
|
console.error("Error deleting variable:", error); |
|
|
return false; |
|
|
} |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expandSystemPromptVariables: async function (str, userId = null) { |
|
|
if (!str) return str; |
|
|
|
|
|
try { |
|
|
const allVariables = await this.getAll(userId); |
|
|
let result = str; |
|
|
|
|
|
|
|
|
const matches = str.match(/\{([^}]+)\}/g) || []; |
|
|
|
|
|
|
|
|
for (const match of matches) { |
|
|
const key = match.substring(1, match.length - 1); |
|
|
|
|
|
|
|
|
if (key.startsWith("user.")) { |
|
|
const userProp = key.split(".")[1]; |
|
|
const variable = allVariables.find((v) => v.key === key); |
|
|
|
|
|
if (variable && typeof variable.value === "function") { |
|
|
if (variable.value.constructor.name === "AsyncFunction") { |
|
|
try { |
|
|
const value = await variable.value(userId); |
|
|
result = result.replace(match, value); |
|
|
} catch (error) { |
|
|
console.error(`Error processing user variable ${key}:`, error); |
|
|
result = result.replace(match, `[User ${userProp}]`); |
|
|
} |
|
|
} else { |
|
|
const value = variable.value(); |
|
|
result = result.replace(match, value); |
|
|
} |
|
|
} else { |
|
|
result = result.replace(match, `[User ${userProp}]`); |
|
|
} |
|
|
continue; |
|
|
} |
|
|
|
|
|
|
|
|
const variable = allVariables.find((v) => v.key === key); |
|
|
if (!variable) continue; |
|
|
|
|
|
|
|
|
if ( |
|
|
["system"].includes(variable.type) && |
|
|
typeof variable.value === "function" |
|
|
) { |
|
|
try { |
|
|
if (variable.value.constructor.name === "AsyncFunction") { |
|
|
const value = await variable.value(userId); |
|
|
result = result.replace(match, value); |
|
|
} else { |
|
|
const value = variable.value(); |
|
|
result = result.replace(match, value); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error(`Error processing dynamic variable ${key}:`, error); |
|
|
result = result.replace(match, match); |
|
|
} |
|
|
} else { |
|
|
result = result.replace(match, variable.value || match); |
|
|
} |
|
|
} |
|
|
return result; |
|
|
} catch (error) { |
|
|
console.error("Error in expandSystemPromptVariables:", error); |
|
|
return str; |
|
|
} |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_checkVariableKey: async function (key = null, checkExisting = true) { |
|
|
if (!key) throw new Error("Key is required"); |
|
|
if (typeof key !== "string") throw new Error("Key must be a string"); |
|
|
if (!/^[a-zA-Z0-9_]+$/.test(key)) |
|
|
throw new Error("Key must contain only letters, numbers and underscores"); |
|
|
if (key.length > 255) |
|
|
throw new Error("Key must be less than 255 characters"); |
|
|
if (key.length < 3) throw new Error("Key must be at least 3 characters"); |
|
|
if (key.startsWith("user.")) |
|
|
throw new Error("Key cannot start with 'user.'"); |
|
|
if (key.startsWith("system.")) |
|
|
throw new Error("Key cannot start with 'system.'"); |
|
|
if (checkExisting && (await this.get(key)) !== null) |
|
|
throw new Error("System prompt variable with this key already exists"); |
|
|
|
|
|
return true; |
|
|
}, |
|
|
}; |
|
|
|
|
|
module.exports = { SystemPromptVariables }; |
|
|
|