@sema-agent/core
Version:
Stateless, task-oriented AI agent core
88 lines (87 loc) • 3.07 kB
JavaScript
function joinParts(parts) {
return parts.filter((p) => Boolean(p && p.trim())).join("\n\n");
}
export function validatePack(pack) {
const ids = new Set();
const ranks = new Set();
for (const s of pack.sections) {
if (!s.id || !s.id.trim())
throw new Error(`prompt-pack ${pack.packId}: section with empty id`);
if (ids.has(s.id))
throw new Error(`prompt-pack ${pack.packId}: duplicate section id "${s.id}"`);
ids.add(s.id);
if (ranks.has(s.rank))
throw new Error(`prompt-pack ${pack.packId}: duplicate rank ${s.rank} ("${s.id}")`);
ranks.add(s.rank);
if (s.blockJoiner !== undefined && s.blockJoiner !== "\n" && s.blockJoiner !== "\n\n") {
throw new Error(`prompt-pack ${pack.packId}: section "${s.id}" has invalid blockJoiner`);
}
}
}
function renderSection(s, inputs) {
if (s.admit && !s.admit(inputs))
return undefined;
const text = s.content(inputs);
if (!text || !text.trim())
return undefined;
return {
id: s.id,
slot: s.slot,
rank: s.rank,
owner: s.owner,
trust: s.trust,
mutability: s.mutability,
cadence: s.cadence,
cacheClass: s.cacheClass,
carrier: s.carrier,
text,
blockId: s.blockId,
blockJoiner: s.blockJoiner ?? "\n\n",
...(s.declaredContentHash ? { declaredContentHash: s.declaredContentHash } : {}),
...(s.origin ? { origin: s.origin } : {}),
};
}
export function compose(pack, inputs) {
validatePack(pack);
const ordered = [...pack.sections].sort((a, b) => a.rank - b.rank);
const sections = [];
for (const s of ordered) {
const compiled = renderSection(s, inputs);
if (compiled)
sections.push(compiled);
}
const text = joinParts(sections.map((s) => s.text));
const blocks = [];
for (const s of sections) {
const last = blocks[blocks.length - 1];
if (last && last.id === s.blockId) {
last.text = `${last.text}${s.blockJoiner}${s.text}`;
}
else {
blocks.push({ id: s.blockId, text: s.text });
}
}
return { text, sections, blocks };
}
export const SINGLE_BLOCK_LAYOUT = { groups: [{ groupId: "body", untilRank: Number.POSITIVE_INFINITY, cacheControlBoundary: true }] };
export function layoutSystemBlocks(composed, layout = SINGLE_BLOCK_LAYOUT) {
const blocks = [];
let g = 0;
let bucket = [];
const flush = () => {
if (bucket.length > 0) {
const grp = layout.groups[Math.min(g, layout.groups.length - 1)];
blocks.push({ groupId: grp.groupId, text: bucket.join("\n\n"), cacheControlBoundary: grp.cacheControlBoundary });
bucket = [];
}
};
for (const s of composed.sections) {
while (g < layout.groups.length - 1 && s.rank > layout.groups[g].untilRank) {
flush();
g++;
}
bucket.push(s.text);
}
flush();
return blocks;
}