@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
183 lines • 7.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getModelVisibleSessionItems = getModelVisibleSessionItems;
exports.hasCompleteApprovalGroups = hasCompleteApprovalGroups;
exports.normalizeItemsForSessionPersistence = normalizeItemsForSessionPersistence;
exports.sessionItemArraysMatch = sessionItemArraysMatch;
exports.agentItemRangeMatches = agentItemRangeMatches;
const base64_1 = require("../utils/base64.js");
const binary_1 = require("../utils/binary.js");
const items_1 = require("./items.js");
const toolResultCorrelation_1 = require("./toolResultCorrelation.js");
/** Return model-visible representations only when every stored occurrence is covered in order. */
function getModelVisibleSessionItems(session, stored, exchange) {
const comparable = (items) => {
// Normalize each record separately: persistence's whole-array deduplication would lose counts.
const normalized = items.map((item) => normalizeItemsForSessionPersistence([item])[0]);
return (session.prepareHistoryItemsForPersistenceComparison?.(structuredClone(normalized)) ?? normalized);
};
const history = comparable(stored);
const modelItems = comparable(exchange.items);
if (history.length !== stored.length ||
modelItems.length !== exchange.items.length) {
return undefined;
}
const visible = [];
let cursor = 0;
for (const item of history) {
const replayItem = (0, items_1.stripReasoningItemIdForPolicy)(item, exchange.reasoningItemIdPolicy);
while (cursor < modelItems.length &&
!sessionItemArraysMatch([item], [modelItems[cursor]]) &&
!sessionItemArraysMatch([replayItem], [modelItems[cursor]]) &&
!sessionItemArraysMatch([item], [
(0, items_1.stripReasoningItemIdForPolicy)(modelItems[cursor], exchange.reasoningItemIdPolicy),
])) {
cursor++;
}
if (cursor === modelItems.length)
return undefined;
visible.push(exchange.items[cursor++]);
}
return visible;
}
/** Check the covered history before automatically compacting an approval group. */
function hasCompleteApprovalGroups(items) {
const pending = new Map();
for (const item of items) {
if (item.type !== 'function_call' &&
item.type !== 'function_call_result' &&
item.type !== 'hosted_tool_call') {
continue;
}
const call = (0, toolResultCorrelation_1.getToolResultCorrelationForCall)(item);
const correlation = call ?? (0, toolResultCorrelation_1.getToolResultCorrelationForResult)(item);
if (!correlation)
continue;
const key = (0, toolResultCorrelation_1.getToolResultCorrelationKey)(correlation);
const count = pending.get(key) ?? 0;
if (call) {
pending.set(key, count + 1);
}
else if (count === 0) {
return false;
}
else if (count === 1) {
pending.delete(key);
}
else {
pending.set(key, count - 1);
}
}
return pending.size === 0;
}
function normalizeItemsForSessionPersistence(items) {
return (0, items_1.deduplicateAgentInputItemsPreferringLatest)(items.map((item) => sanitizeValueForSession(stripTransientCallIds(item))));
}
function sessionItemArraysMatch(items, expected) {
return (items.length === expected.length &&
agentItemRangeMatches(items, expected, 0));
}
function agentItemRangeMatches(items, expected, start) {
if (start < 0 || start + expected.length > items.length) {
return false;
}
return expected.every((item, offset) => getSessionReconciliationItemKey(items[start + offset]) ===
getSessionReconciliationItemKey(item));
}
function sanitizeValueForSession(value, context = {}) {
if (value === null || value === undefined) {
return value;
}
const binary = (0, binary_1.toUint8ArrayFromBinary)(value);
if (binary) {
return toDataUrlFromBytes(binary, context.mediaType);
}
if (Array.isArray(value)) {
return value.map((entry) => sanitizeValueForSession(entry, context));
}
if (!isPlainObject(value)) {
return value;
}
const record = value;
const result = {};
const mediaType = typeof record.mediaType === 'string' && record.mediaType.length > 0
? record.mediaType
: context.mediaType;
for (const [key, entry] of Object.entries(record)) {
const nextContext = key === 'data' || key === 'fileData' ? { mediaType } : context;
result[key] = sanitizeValueForSession(entry, nextContext);
}
return result;
}
function toDataUrlFromBytes(bytes, mediaType) {
const base64 = (0, base64_1.encodeUint8ArrayToBase64)(bytes);
const type = mediaType && !mediaType.startsWith('data:') ? mediaType : 'text/plain';
return `data:${type};base64,${base64}`;
}
function stripTransientCallIds(value) {
if (value === null || value === undefined) {
return value;
}
if (Array.isArray(value)) {
return value.map((entry) => stripTransientCallIds(entry));
}
if (!isPlainObject(value)) {
return value;
}
const record = value;
const result = {};
const isProtocolItem = typeof record.type === 'string' && record.type.length > 0;
const shouldStripId = isProtocolItem && shouldStripIdForProtocolItem(record);
for (const [key, entry] of Object.entries(record)) {
if (shouldStripId && key === 'id') {
continue;
}
result[key] = stripTransientCallIds(entry);
}
return result;
}
function shouldStripIdForProtocolItem(record) {
switch (record.type) {
case 'function_call':
case 'function_call_result':
return true;
case 'tool_search_call':
case 'tool_search_output':
return hasToolSearchCallId(record);
default:
return false;
}
}
function hasToolSearchCallId(record) {
const topLevelCallId = record.call_id ?? record.callId;
if (typeof topLevelCallId === 'string' && topLevelCallId.length > 0) {
return true;
}
const providerData = isPlainObject(record.providerData)
? record.providerData
: undefined;
const providerCallId = providerData?.call_id ?? providerData?.callId;
return typeof providerCallId === 'string' && providerCallId.length > 0;
}
function getSessionReconciliationItemKey(item) {
return JSON.stringify(sortSessionReconciliationValue(item));
}
function sortSessionReconciliationValue(value) {
if (Array.isArray(value)) {
return value.map(sortSessionReconciliationValue);
}
if (!isPlainObject(value)) {
return value;
}
return Object.fromEntries(Object.keys(value)
.sort()
.map((key) => [key, sortSessionReconciliationValue(value[key])]));
}
function isPlainObject(value) {
if (typeof value !== 'object' || value === null) {
return false;
}
const proto = Object.getPrototypeOf(value);
return proto === Object.prototype || proto === null;
}
//# sourceMappingURL=sessionItems.js.map