@sema-agent/core
Version:
Stateless, task-oriented AI agent core
107 lines (106 loc) • 4.98 kB
JavaScript
import { isTextBlock, renderContentBlock } from "../../internal/llm.js";
import { isMediaBlock, mediaBlockDescriptor, mediaBudgetBytes, omittedMediaDescriptor } from "../media-byte-cap.js";
import { engineCardTypes } from "../tool-registry.js";
import { readMember } from "../thrown-value.js";
const MAX_TOOL_END_OUTPUT_CHARS = 16384;
const marker = (n) => `…[+${n} chars truncated]`;
export const toolOutputBlockChars = (b) => isTextBlock(b) ? b.text.length + (JSON.stringify({ ...b, type: undefined, text: undefined })?.length ?? 2) - 2 : isMediaBlock(b) ? b.data.length : (JSON.stringify(b)?.length ?? 0);
const boundTextLane = (blocks, lane) => {
const sized = lane.map((i) => ({ i, size: toolOutputBlockChars(blocks[i]) }));
const stream = sized.reduce((n, { size }, k) => n + size + (k > 0 ? 1 : 0), 0);
const forms = new Map();
let used = 0;
for (const [k, { i, size }] of sized.entries()) {
const sep = k > 0 ? 1 : 0;
if (used + sep + size <= MAX_TOOL_END_OUTPUT_CHARS) {
used += sep + size;
forms.set(i, blocks[i]);
continue;
}
const shown = renderContentBlock(blocks[i]).slice(0, Math.max(0, MAX_TOOL_END_OUTPUT_CHARS - used - sep));
const notShown = stream - Math.min(MAX_TOOL_END_OUTPUT_CHARS, used + sep) - (isTextBlock(blocks[i]) ? shown.length : 0);
forms.set(i, { type: "text", text: shown + marker(notShown) });
return { forms, cut: true };
}
return { forms, cut: false };
};
const wireBytes = (b) => Buffer.byteLength(JSON.stringify(b)) + 1;
const SUMMARY_RESERVE = wireBytes({ type: "omitted", blocks: Number.MAX_SAFE_INTEGER, bytes: Number.MAX_SAFE_INTEGER, truncated: true }) + 2;
const fitMedia = (media, budget) => {
let used = 0;
let degraded = false;
const blocks = [];
for (const [i, m] of media.entries()) {
const cost = used + m.data.length >= budget ? Infinity : wireBytes(m);
const kept = used + cost <= budget;
const out = kept ? m : mediaBlockDescriptor(m);
const outCost = kept ? cost : wireBytes(out);
degraded ||= !kept;
if (used + outCost > budget) {
blocks.push(omittedMediaDescriptor(media.slice(i)));
break;
}
used += outCost;
blocks.push(out);
}
return { blocks, degraded };
};
const project = (content, mediaByteCapBytes) => {
if (!Array.isArray(content)) {
const s = typeof content === "string" ? content : (JSON.stringify(content) ?? "");
if (s.length <= MAX_TOOL_END_OUTPUT_CHARS)
return { output: content, truncated: false };
return { output: s.slice(0, MAX_TOOL_END_OUTPUT_CHARS) + marker(s.length - MAX_TOOL_END_OUTPUT_CHARS), truncated: true, totalChars: s.length };
}
const media = content.map(isMediaBlock);
const text = boundTextLane(content, content.flatMap((_, i) => (media[i] ? [] : [i])));
const limit = mediaBudgetBytes(mediaByteCapBytes);
const textBytes = [...text.forms.values()].reduce((n, f) => n + wireBytes(f), 0);
const fit = fitMedia(content.filter((_, i) => media[i]), limit === Infinity ? Infinity : Math.ceil((limit * 4) / 3) - SUMMARY_RESERVE - textBytes);
if (!text.cut && !fit.degraded)
return { output: content, truncated: false };
let next = 0;
const output = content.flatMap((_, i) => (media[i] ? (fit.blocks[next++] ?? []) : text.forms.has(i) ? [text.forms.get(i)] : []));
return { output, truncated: true, totalChars: content.reduce((n, b) => n + toolOutputBlockChars(b), 0) };
};
export const toolOutputFrom = (result, mediaByteCapBytes) => {
try {
const content = result !== null && typeof result === "object" ? result.content : result;
return content === undefined ? undefined : project(content, mediaByteCapBytes);
}
catch {
return { output: "[unserializable tool output]", truncated: true };
}
};
const CC_DETAIL_TYPES = engineCardTypes();
export function createStructuredProjector(opts = {}) {
const allowed = new Set([...CC_DETAIL_TYPES, ...(opts.declaredCards ?? [])]);
const announced = new Set();
return (result) => {
const details = readMember(result, "details");
if (details === null || typeof details !== "object")
return undefined;
const t = readMember(details, "type");
if (typeof t !== "string")
return undefined;
if (!allowed.has(t)) {
if (opts.onUndeclared !== undefined && !announced.has(t)) {
announced.add(t);
opts.onUndeclared(t);
}
return undefined;
}
return boundedCard(details);
};
}
function boundedCard(details) {
try {
const serialized = JSON.stringify(details) ?? "";
if (serialized.length > MAX_TOOL_END_OUTPUT_CHARS * 4)
return undefined;
}
catch {
return undefined;
}
return details;
}