@sema-agent/core
Version:
Stateless, task-oriented AI agent core
115 lines (114 loc) • 3.13 kB
JavaScript
import { RESPONSES_APIS } from "./reasoning.js";
export const OPENAI_RESERVED = new Set([
"model",
"messages",
"stream",
"stream_options",
"tools",
"temperature",
"max_tokens",
"stop",
"reasoning_effort",
"thinking",
"reasoning",
"enable_thinking",
"chat_template_kwargs",
]);
export const ANTHROPIC_RESERVED = new Set([
"model",
"messages",
"system",
"stream",
"tools",
"temperature",
"max_tokens",
"thinking",
"output_config",
"context_management",
]);
export const RESPONSES_RESERVED = new Set([
"model",
"input",
"stream",
"instructions",
"tools",
"temperature",
"max_output_tokens",
"reasoning",
"store",
"previous_response_id",
"conversation",
]);
export function reservedFor(api) {
if (RESPONSES_APIS.has(api))
return RESPONSES_RESERVED;
return api === "anthropic-messages" ? ANTHROPIC_RESERVED : OPENAI_RESERVED;
}
export function applyExtraBody(body, extraBody, reserved) {
if (!extraBody)
return body;
const passthrough = {};
for (const [k, v] of Object.entries(extraBody)) {
if (!reserved.has(k))
passthrough[k] = v;
}
return { ...passthrough, ...body };
}
export const AUTH_CARRIER_NAMES = new Set(["authorization", "x-api-key"]);
export function lockHeader(headers, lowerName, value) {
for (const k of Object.keys(headers)) {
if (k !== lowerName && k.toLowerCase() === lowerName)
delete headers[k];
}
headers[lowerName] = value;
}
export function takeHeaderCasefold(headers, lowerName) {
for (const k of Object.keys(headers)) {
if (k.toLowerCase() === lowerName) {
const v = headers[k];
delete headers[k];
return v;
}
}
return undefined;
}
export function mergeHeaders(...bags) {
const out = {};
const spellingByFold = new Map();
for (const bag of bags) {
if (!bag)
continue;
for (const [name, value] of Object.entries(bag)) {
const fold = name.toLowerCase();
if (AUTH_CARRIER_NAMES.has(fold)) {
out[name] = value;
continue;
}
const prior = spellingByFold.get(fold);
if (prior !== undefined && prior !== name)
delete out[prior];
spellingByFold.set(fold, name);
out[name] = value;
}
}
return out;
}
export const OUTPUT_CAP_KEYS = {
openai: ["max_tokens", "max_completion_tokens"],
anthropic: ["max_tokens"],
responses: ["max_output_tokens"],
};
export function effectiveOutputCap(body, keys) {
let smallest;
for (const key of keys) {
const raw = body[key];
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0)
continue;
if (smallest === undefined || raw < smallest)
smallest = raw;
}
return smallest;
}
export function reservedCollisions(extraBody, reserved) {
return extraBody ? Object.keys(extraBody).filter((k) => reserved.has(k)) : [];
}