q / core /schema.js
Humuhumu33's picture
Upload folder using huggingface_hub
3365e13 verified
Raw
History Blame Contribute Delete
4.17 kB
// core/schema.js — lean, dependency-free validators ("zod-lite") for the LibreChat-faithful
// Conversation / Message / Preset shapes. Field names mirror LibreChat's data model (packages/
// data-schemas) so an exported conversation interoperates; values are validated/normalised, not
// hashed (the κ-object store is what makes them content-addressed). No build step, no dep.
const isStr = (v) => typeof v === "string";
const isBool = (v) => typeof v === "boolean";
const isNum = (v) => typeof v === "number" && !Number.isNaN(v);
const arr = (v) => (Array.isArray(v) ? v : []);
const clampNum = (v, lo, hi, d) => (isNum(v) ? Math.min(hi, Math.max(lo, v)) : d);
// A chat message (one node in the parentMessageId tree).
export function normMessage(m = {}) {
return {
messageId: isStr(m.messageId) ? m.messageId : null,
conversationId: isStr(m.conversationId) ? m.conversationId : null,
parentMessageId: isStr(m.parentMessageId) ? m.parentMessageId : null, // tree edge
sender: isStr(m.sender) ? m.sender : (m.isCreatedByUser ? "User" : "Assistant"),
isCreatedByUser: isBool(m.isCreatedByUser) ? m.isCreatedByUser : false,
model: isStr(m.model) ? m.model : null,
text: isStr(m.text) ? m.text : "",
content: arr(m.content), // structured parts (text/tool/image/reasoning)
tokenCount: isNum(m.tokenCount) ? m.tokenCount : null,
error: m.error || null,
unfinished: !!m.unfinished,
feedback: m.feedback && isStr(m.feedback.rating || "") ? { rating: m.feedback.rating, tag: m.feedback.tag || null, text: m.feedback.text || "" } : (m.feedback || null),
files: arr(m.files),
createdAt: isStr(m.createdAt) ? m.createdAt : null,
receiptKappa: isStr(m.receiptKappa) ? m.receiptKappa : null, // links the PROV-O inference receipt
};
}
// A conversation (spreads the active preset, like LibreChat's conversationPreset).
export function normConversation(c = {}) {
return {
conversationId: isStr(c.conversationId) ? c.conversationId : null,
title: isStr(c.title) ? c.title : "New Chat",
tags: arr(c.tags),
files: arr(c.files),
folder: isStr(c.folder) ? c.folder : null,
archived: !!c.archived,
favorite: !!c.favorite,
isTemporary: !!c.isTemporary,
preset: c.preset ? normPreset(c.preset) : null,
createdAt: isStr(c.createdAt) ? c.createdAt : null,
updatedAt: isStr(c.updatedAt) ? c.updatedAt : null,
};
}
// A preset / parameter bundle. Bounds mirror common LLM ranges; model is the local κ-object id.
export function normPreset(p = {}) {
return {
presetId: isStr(p.presetId) ? p.presetId : null,
title: isStr(p.title) ? p.title : "Preset",
model: isStr(p.model) ? p.model : null,
temperature: clampNum(p.temperature, 0, 2, 0),
top_p: clampNum(p.top_p, 0, 1, 1),
top_k: clampNum(p.top_k, 0, 200, 40),
maxOutputTokens: clampNum(p.maxOutputTokens, 1, 8192, 1024),
promptPrefix: isStr(p.promptPrefix) ? p.promptPrefix : "",
presence_penalty: clampNum(p.presence_penalty, -2, 2, 0),
frequency_penalty: clampNum(p.frequency_penalty, -2, 2, 0),
stop: arr(p.stop),
iconURL: isStr(p.iconURL) ? p.iconURL : null,
isArchived: !!p.isArchived,
};
}
// Walk a flat message list into the LibreChat parentMessageId tree → {roots, byId, childrenOf}.
export function buildTree(messages) {
const byId = new Map(); const childrenOf = new Map(); const roots = [];
for (const m of messages) { byId.set(m.messageId, m); childrenOf.set(m.messageId, []); }
for (const m of messages) {
if (m.parentMessageId && childrenOf.has(m.parentMessageId)) childrenOf.get(m.parentMessageId).push(m.messageId);
else roots.push(m.messageId);
}
return { roots, byId, childrenOf };
}
// The active thread = follow the chosen child at each branch from a root to a leaf.
export function threadOf(tree, { choose = (siblings) => siblings[siblings.length - 1] } = {}) {
const out = []; let cur = tree.roots.length ? choose(tree.roots) : null;
while (cur) { out.push(tree.byId.get(cur)); const kids = tree.childrenOf.get(cur) || []; cur = kids.length ? choose(kids) : null; }
return out;
}