@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
325 lines • 12.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemorySession = void 0;
const _shims_1 = require("@openai/agents-core/_shims");
const historyMutations_1 = require("./historyMutations.js");
const errors_1 = require("../errors.js");
const logger_1 = require("../logger.js");
const protocol_1 = require("../types/protocol.js");
/**
* Simple in-memory session store intended for demos or tests. Not recommended for production use.
*/
class MemorySession {
sessionId;
logger;
items;
appliedHistoryTransactions = new Map();
constructor(options = {}) {
this.sessionId = options.sessionId ?? (0, _shims_1.randomUUID)();
this.items = options.initialItems
? options.initialItems.map(cloneAgentItem)
: [];
this.logger = options.logger ?? logger_1.logger;
}
async getSessionId() {
return this.sessionId;
}
async getItems(limit) {
if (limit === undefined) {
const cloned = this.items.map(cloneAgentItem);
if (!this.logger.dontLogModelData && !this.logger.dontLogToolData) {
this.logger.debug(`Getting items from memory session (${this.sessionId}): ${JSON.stringify(cloned)}`);
}
return cloned;
}
if (limit <= 0) {
return [];
}
const start = Math.max(this.items.length - limit, 0);
const items = this.items.slice(start).map(cloneAgentItem);
if (!this.logger.dontLogModelData && !this.logger.dontLogToolData) {
this.logger.debug(`Getting items from memory session (${this.sessionId}): ${JSON.stringify(items)}`);
}
return items;
}
async addItems(items) {
if (items.length === 0) {
return;
}
const cloned = items.map(cloneAgentItem);
if (!this.logger.dontLogModelData && !this.logger.dontLogToolData) {
this.logger.debug(`Adding items to memory session (${this.sessionId}): ${JSON.stringify(cloned)}`);
}
this.items = [...this.items, ...cloned];
}
async popItem() {
if (this.items.length === 0) {
return undefined;
}
const item = this.items[this.items.length - 1];
const cloned = cloneAgentItem(item);
if (!this.logger.dontLogModelData && !this.logger.dontLogToolData) {
this.logger.debug(`Popping item from memory session (${this.sessionId}): ${JSON.stringify(cloned)}`);
}
this.items = this.items.slice(0, -1);
return cloned;
}
async clearSession() {
this.logger.debug(`Clearing memory session (${this.sessionId})`);
this.items = [];
this.appliedHistoryTransactions = new Map();
}
async applyHistoryMutations(args) {
if (args.mutations.length === 0) {
return;
}
this.items = (0, historyMutations_1.applySessionHistoryMutations)(this.items, args.mutations);
}
async applyHistoryTransaction(args) {
const { operationId, transaction } = snapshotHistoryTransactionArgs(args);
const existing = this.appliedHistoryTransactions.get(operationId);
if (existing) {
if (!structurallyEqual(existing, transaction)) {
throw new errors_1.UserError('Session history operation was already applied with a different transaction.');
}
return;
}
let nextItems;
if (transaction.type === 'append_items') {
nextItems = [...this.items, ...transaction.items.map(cloneAgentItem)];
}
else {
const { expectedSuffix, replacement } = transaction;
const suffixStart = this.items.length - expectedSuffix.length;
const actualSuffix = suffixStart < 0 ? [] : this.items.slice(suffixStart);
const actualSuffixSnapshot = trySnapshotAgentItems(actualSuffix);
if (suffixStart < 0 ||
!actualSuffixSnapshot ||
!structurallyEqual(actualSuffixSnapshot, expectedSuffix)) {
throw new errors_1.UserError('Session history suffix no longer matches the transaction precondition.');
}
nextItems = [
...this.items.slice(0, suffixStart),
...replacement.map(cloneAgentItem),
];
}
const nextTransactions = new Map(this.appliedHistoryTransactions);
nextTransactions.set(operationId, transaction);
this.items = nextItems;
this.appliedHistoryTransactions = nextTransactions;
}
}
exports.MemorySession = MemorySession;
function cloneAgentItem(item) {
return structuredClone(item);
}
function snapshotHistoryTransactionArgs(args) {
let operationId;
let transaction;
try {
if (!args || typeof args !== 'object') {
throw new Error('Missing transaction arguments.');
}
operationId = Reflect.get(args, 'operationId');
transaction = Reflect.get(args, 'transaction');
}
catch {
throw new errors_1.UserError('Session history transaction arguments are invalid.');
}
if (typeof operationId !== 'string' || !operationId.trim()) {
throw new errors_1.UserError('Session history transaction operationId must be a non-empty string.');
}
if (!transaction || typeof transaction !== 'object') {
throw new errors_1.UserError('Session history transaction must be an object.');
}
return {
operationId,
transaction: snapshotHistoryTransaction(transaction),
};
}
function snapshotHistoryTransaction(transaction) {
let snapshot;
try {
snapshot = snapshotSupportedValue(transaction);
structuredClone(transaction);
}
catch {
throw new errors_1.UserError('Session history transaction items contain invalid or unsupported data.');
}
if (!snapshot || typeof snapshot !== 'object') {
throw new errors_1.UserError('Session history transaction must be an object.');
}
const record = snapshot;
if (record.type === 'append_items') {
if (Object.keys(record).length !== 2 ||
!Object.prototype.hasOwnProperty.call(record, 'type') ||
!Object.prototype.hasOwnProperty.call(record, 'items') ||
!Array.isArray(record.items)) {
throw new errors_1.UserError('Session history append transaction fields are invalid.');
}
try {
return {
type: record.type,
items: snapshotAgentItems(record.items),
};
}
catch {
throw new errors_1.UserError('Session history transaction items contain invalid or unsupported data.');
}
}
if (record.type !== 'replace_suffix') {
throw new errors_1.UserError('Unsupported session history transaction type.');
}
if (Object.keys(record).length !== 3 ||
!Object.prototype.hasOwnProperty.call(record, 'type') ||
!Object.prototype.hasOwnProperty.call(record, 'expectedSuffix') ||
!Object.prototype.hasOwnProperty.call(record, 'replacement') ||
!Array.isArray(record.expectedSuffix) ||
!Array.isArray(record.replacement)) {
throw new errors_1.UserError('Session history suffix transaction fields are invalid.');
}
try {
return {
type: record.type,
expectedSuffix: snapshotAgentItems(record.expectedSuffix),
replacement: snapshotAgentItems(record.replacement),
};
}
catch {
throw new errors_1.UserError('Session history transaction items contain invalid or unsupported data.');
}
}
function snapshotAgentItems(items) {
const snapshots = snapshotSupportedValue(items);
return snapshots.map((snapshot) => {
if (!protocol_1.ModelItem.safeParse(snapshot).success) {
throw new Error('Invalid session history item.');
}
return snapshot;
});
}
function trySnapshotAgentItems(items) {
try {
return snapshotAgentItems(items);
}
catch {
return undefined;
}
}
function snapshotSupportedValue(value, ancestors = new WeakSet()) {
if (value === null ||
value === undefined ||
typeof value === 'string' ||
typeof value === 'boolean' ||
typeof value === 'number') {
return value;
}
if (typeof value !== 'object') {
throw new Error('Unsupported session history value.');
}
if (value instanceof Uint8Array) {
const keys = Reflect.ownKeys(value);
if (keys.length !== value.length ||
keys.some((key) => typeof key !== 'string' || !isArrayIndexForLength(key, value.length))) {
throw new Error('Unsupported session history binary property.');
}
return new Uint8Array(value);
}
if (ancestors.has(value)) {
throw new Error('Cyclic session history value.');
}
ancestors.add(value);
try {
if (Array.isArray(value)) {
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, 'length');
if (!lengthDescriptor || !('value' in lengthDescriptor)) {
throw new Error('Unsupported session history array.');
}
const length = lengthDescriptor.value;
const keys = Reflect.ownKeys(value);
if (typeof length !== 'number' ||
!Number.isSafeInteger(length) ||
length < 0 ||
keys.length !== length + 1 ||
keys.some((key) => typeof key !== 'string' ||
(key !== 'length' && !isArrayIndexForLength(key, length)))) {
throw new Error('Unsupported session history array.');
}
const snapshot = new Array(length);
for (let index = 0; index < length; index++) {
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
if (!descriptor?.enumerable || !('value' in descriptor)) {
throw new Error('Unsupported session history array item.');
}
snapshot[index] = snapshotSupportedValue(descriptor.value, ancestors);
}
return snapshot;
}
const prototype = Object.getPrototypeOf(value);
if (prototype !== Object.prototype && prototype !== null) {
throw new Error('Unsupported session history object.');
}
const snapshot = {};
for (const key of Reflect.ownKeys(value)) {
if (typeof key !== 'string') {
throw new Error('Unsupported session history property.');
}
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (!descriptor?.enumerable || !('value' in descriptor)) {
throw new Error('Unsupported session history property.');
}
Object.defineProperty(snapshot, key, {
configurable: true,
enumerable: true,
value: snapshotSupportedValue(descriptor.value, ancestors),
writable: true,
});
}
return snapshot;
}
finally {
ancestors.delete(value);
}
}
function isArrayIndexForLength(key, length) {
if (!/^(0|[1-9]\d*)$/.test(key)) {
return false;
}
const index = Number(key);
return Number.isSafeInteger(index) && index >= 0 && index < length;
}
function structurallyEqual(left, right) {
if (Object.is(left, right)) {
return true;
}
if (Array.isArray(left) || Array.isArray(right)) {
return (Array.isArray(left) &&
Array.isArray(right) &&
left.length === right.length &&
left.every((entry, index) => structurallyEqual(entry, right[index])));
}
if (left instanceof Uint8Array || right instanceof Uint8Array) {
return (left instanceof Uint8Array &&
right instanceof Uint8Array &&
left.length === right.length &&
left.every((byte, index) => byte === right[index]));
}
if (left === null ||
right === null ||
typeof left !== 'object' ||
typeof right !== 'object') {
return false;
}
if (Object.getPrototypeOf(left) !== Object.prototype ||
Object.getPrototypeOf(right) !== Object.prototype) {
return false;
}
const leftRecord = left;
const rightRecord = right;
const leftKeys = Object.keys(leftRecord).sort();
const rightKeys = Object.keys(rightRecord).sort();
return (leftKeys.length === rightKeys.length &&
leftKeys.every((key, index) => key === rightKeys[index] &&
structurallyEqual(leftRecord[key], rightRecord[key])));
}
//# sourceMappingURL=memorySession.js.map