UNPKG

@openai/agents-core

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.

550 lines • 19.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamedRunResult = exports.RunResult = void 0; const _shims_1 = require("@openai/agents-core/_shims"); const items_1 = require("./runner/items.js"); const logger_1 = __importStar(require("./logger.js")); const protocol_1 = require("./types/protocol.js"); const abortSignals_1 = require("./utils/abortSignals.js"); const finalOutputError_1 = require("./utils/finalOutputError.js"); function createAbortHandlerRef() { const ref = {}; const handler = () => { ref.current?.(); }; return { ref, handler }; } class RunResultBase { state; constructor(state) { this.state = state; } /** * The history of the agent run. This includes the input items and the new items generated during * the agent run. * * This can be used as inputs for the next agent run. */ get history() { return (0, items_1.getTurnInput)(this.input, this.newItems, this.state._reasoningItemIdPolicy); } /** * The new items generated during the agent run. These include things like new messages, tool * calls and their outputs, etc. * * It does not include information about the agents and instead represents the model data. * * For the output including the agents, use the `newItems` property. */ get output() { return (0, items_1.getRunOutput)(this.newItems, this.state._reasoningItemIdPolicy); } /** * A copy of the original input items. */ get input() { return this.state._originalInput; } /** * The public run context for this run. */ get runContext() { return this.state._context; } /** * Metadata about the nested `Agent.asTool()` invocation that produced this result, when applicable. */ get agentToolInvocation() { return this.state._agentToolInvocation; } /** * The run items generated during the agent run. This associates the model data with the agents. * * For the model data that can be used as inputs for the next agent run, use the `output` property. */ get newItems() { return this.state._generatedItems; } /** * The raw LLM responses generated by the model during the agent run. */ get rawResponses() { return this.state._modelResponses; } /** * The last response ID generated by the model during the agent run. */ get lastResponseId() { const responses = this.rawResponses; return responses && responses.length > 0 ? responses[responses.length - 1].responseId : undefined; } /** * The last agent that was run */ get lastAgent() { return this.state._currentAgent; } /** * The agent that should handle the next turn. * This is an alias for the last agent that completed a turn. */ get activeAgent() { return this.lastAgent; } /** * Guardrail results for the input messages. */ get inputGuardrailResults() { return this.state._inputGuardrailResults; } /** * Guardrail results for the final output of the agent. */ get outputGuardrailResults() { return this.state._outputGuardrailResults; } /** * Guardrail results for tool inputs. */ get toolInputGuardrailResults() { return this.state._toolInputGuardrailResults; } /** * Guardrail results for tool outputs. */ get toolOutputGuardrailResults() { return this.state._toolOutputGuardrailResults; } /** * Any interruptions that occurred during the agent run for example for tool approvals. */ get interruptions() { return this.state.getInterruptions(); } /** * The final output of the agent. If the output type was set to anything other than `text`, * this will be parsed either as JSON or using the validation schema you provided. */ get finalOutput() { if (this.state._currentStep?.type === 'next_step_final_output') { const output = this.state._currentStep.output; if (this.state._currentAgent.outputType === 'text') { return this.state._currentAgent.processFinalOutput(output); } return (0, finalOutputError_1.processFinalOutputWithRedaction)(() => this.state._currentAgent.processFinalOutput(output)); } logger_1.default.warn('Accessed finalOutput before agent run is completed.'); return undefined; } } /** * The result of an agent run. */ class RunResult extends RunResultBase { constructor(state) { super(state); } } exports.RunResult = RunResult; /** * The result of an agent run in streaming mode. */ class StreamedRunResult extends RunResultBase { #finalOutputHidden = false; get finalOutput() { if (this.#finalOutputHidden) { logger_1.default.warn('Accessed finalOutput before agent run is completed.'); return undefined; } return super.finalOutput; } /** @internal */ _hideFinalOutput() { this.#finalOutputHidden = true; } /** @internal */ _revealFinalOutput() { this.#finalOutputHidden = false; } /** * The current agent that is running */ get currentAgent() { return this.lastAgent; } /** * The number of model turns admitted so far. * * Written by the streaming runner at the point a turn is *admitted* -- after * the `maxTurns` limit check and any blocking input guardrails have passed, * immediately before the model request starts. It therefore counts turns that * actually reached the model, not turns that were merely begun: * * - a handled max-turn boundary (e.g. `maxTurns: 0`) leaves this at `0`, * because the limit check throws before any turn is admitted; * - a blocking input guardrail that trips on the first turn leaves this at * `0`, because no model request was made; * - a resumed run starts from the turn count carried in the resumed state. * * `RunState._currentTurn` is incremented at the *start* of a turn, before * either check. The runner rolls that increment back if the model request * never starts, so resumed state carries only admitted turns. */ currentTurn = 0; /** * The maximum number of turns that can be run */ maxTurns; #error = null; #hasError = false; #combinedSignal; #abortSignalSnapshot; #abortController; #readableController; #readableStream; #queuedStreamEvents = []; #queuedStreamEventIndex = 0; #agentToolStreamBuffer; #streamReadPending = false; #streamTerminal; #preserveQueuedItemsOnError = false; #completedPromise; #completedPromiseResolve; #completedPromiseReject; #cancelled = false; #streamLoopPromise; #abortHandler; #abortHandlerRef; #combinedSignalCleanup = () => { }; constructor(result = {}) { super(result.state); this.#finalOutputHidden = result.state?._currentStep?.type === 'next_step_final_output'; // Seed from the resumed state so a run continued from a serialized state does // not restart its public turn count at 0. A fresh run carries `_currentTurn = 0` // here, so this is a no-op for the common case. this.currentTurn = result.state?._currentTurn ?? 0; this.#abortController = new AbortController(); const { signal: combinedSignal, cleanup: cleanupCombinedSignal } = (0, abortSignals_1.combineAbortSignalsWithOptions)([result.signal, this.#abortController.signal], { onAbortSignalAnyError: (error) => { (0, logger_1.logModelAndToolActionDebug)(logger_1.default, 'AbortSignal.any failed, falling back:', error); }, }); this.#combinedSignal = combinedSignal; this.#combinedSignalCleanup = cleanupCombinedSignal; this.#abortSignalSnapshot = combinedSignal; this.#readableStream = new _shims_1.ReadableStream({ start: (controller) => { this.#readableController = controller; }, pull: () => { this.#streamReadPending = true; this.#drainStreamQueue(); }, cancel: () => { if (!this.#abortController.signal.aborted) { this.#abortController.abort(); } this.#cancelStream(); }, }, { highWaterMark: 0, }); this.#completedPromise = new Promise((resolve, reject) => { this.#completedPromiseResolve = resolve; this.#completedPromiseReject = reject; }); if (this.#combinedSignal) { // Create the handler outside this scope so it cannot capture the run via lexical this. const { ref: abortRef, handler: handleAbort } = createAbortHandlerRef(); abortRef.current = () => { this.#handleAbort(); }; this.#abortHandler = handleAbort; this.#abortHandlerRef = abortRef; if (this.#combinedSignal.aborted) { handleAbort(); } else { this.#combinedSignal.addEventListener('abort', handleAbort, { once: true, }); } } } /** * @internal * Adds an item to the stream of output items */ _addItem(item) { if (!this.cancelled && !this.#streamTerminal) { if (this.#agentToolStreamBuffer && this.#queuedStreamEvents.length - this.#queuedStreamEventIndex >= this.#agentToolStreamBuffer.maxPendingEvents) { this.#agentToolStreamBuffer.overflow(); } this.#queuedStreamEvents.push(item); this.#drainStreamQueue(); } } /** @internal Sets the agent-tool budget before its producer starts. */ _setAgentToolStreamBuffer(buffer) { this.#agentToolStreamBuffer = buffer; } /** * @internal * Keeps already queued events available if later cleanup fails. */ _preserveQueuedItemsOnError() { this.#preserveQueuedItemsOnError = true; } /** * @internal * Indicates that the stream has been completed */ _done() { if (!this.cancelled) { if (!this.#streamTerminal) { this.#streamTerminal = { type: 'done' }; } this.#drainStreamQueue(); } this.#completedPromiseResolve?.(); this.#detachAbortHandler(); } /** * @internal * Handles an error in the stream loop. */ _raiseError(err, options) { if (!this.cancelled) { const existingPreservedError = this.#streamTerminal?.type === 'error' && this.#streamTerminal.preserveQueuedItems ? this.#streamTerminal : undefined; const preserveQueuedItems = options?.preserveQueuedItems === true || Boolean(existingPreservedError) || this.#preserveQueuedItemsOnError; this.#streamTerminal = { type: 'error', error: existingPreservedError ? existingPreservedError.error : err, preserveQueuedItems, }; if (!preserveQueuedItems) { this.#clearStreamQueue(); if (this.#readableController) { this.#readableController.error(err); this.#readableController = undefined; } } else { this.#drainStreamQueue(); } } if (!this.#hasError) { this.#hasError = true; this.#error = err; this.#completedPromiseReject?.(err); this.#completedPromise.catch((e) => { (0, logger_1.logModelAndToolActionDebug)(logger_1.default, 'Resulted in an error:', e); }); } this.#detachAbortHandler(); } /** * Returns true if the stream has been cancelled. */ get cancelled() { return this.#cancelled; } /** * Returns the underlying readable stream. * @returns A readable stream of the agent run. */ toStream() { return this.#readableStream; } /** * Await this promise to ensure that the stream has been completed if you are not consuming the * stream directly. */ get completed() { return this.#completedPromise; } /** * Error thrown during the run, if any. */ get error() { return this.#error; } toTextStream(options = {}) { const stream = this.#readableStream.pipeThrough(new _shims_1.TransformStream({ transform(event, controller) { if (event.type === 'raw_model_stream_event' && event.data.type === 'output_text_delta') { const item = protocol_1.StreamEventTextStream.parse(event.data); controller.enqueue(item.delta); } }, })); if (options.compatibleWithNodeStreams) { return _shims_1.Readable.fromWeb(stream); } return stream; } [Symbol.asyncIterator]() { return this.#readableStream[Symbol.asyncIterator](); } /** * @internal * Sets the stream loop promise that completes when the internal stream loop finishes. * This is used to defer trace end until all agent work is complete. */ _setStreamLoopPromise(promise) { this.#streamLoopPromise = promise; } /** * @internal * Returns a promise that resolves when the stream loop completes. * This is used by the tracing system to wait for all agent work before ending the trace. */ _getStreamLoopPromise() { return this.#streamLoopPromise; } /** * @internal * Returns the abort signal that should be used to cancel the streaming run. */ _getAbortSignal() { return this.#abortSignalSnapshot ?? this.#combinedSignal; } #handleAbort() { this.#cancelStream(); } #cancelStream() { if (this.#cancelled) { this.#detachAbortHandler(); return; } this.#cancelled = true; this.#clearStreamQueue(); this.#preserveQueuedItemsOnError = false; this.#streamTerminal = { type: 'done' }; this.#streamReadPending = false; const controller = this.#readableController; this.#readableController = undefined; if (controller) { try { controller.close(); } catch (err) { (0, logger_1.logModelAndToolActionDebug)(logger_1.default, 'Failed to close readable stream on abort:', err); } } this.#detachAbortHandler(); } #drainStreamQueue() { const controller = this.#readableController; if (!controller || !this.#streamReadPending) { return; } if (this.#queuedStreamEventIndex < this.#queuedStreamEvents.length) { const event = this.#queuedStreamEvents[this.#queuedStreamEventIndex]; if (this.#agentToolStreamBuffer) { // Release delivered notifications without copying the pending backlog. this.#queuedStreamEvents[this.#queuedStreamEventIndex] = undefined; } this.#queuedStreamEventIndex += 1; this.#compactStreamQueue(); this.#streamReadPending = false; controller.enqueue(event); return; } if (this.#streamTerminal?.type === 'done') { this.#streamReadPending = false; controller.close(); this.#readableController = undefined; return; } if (this.#streamTerminal?.type === 'error') { this.#streamReadPending = false; controller.error(this.#streamTerminal.error); this.#readableController = undefined; } } #clearStreamQueue() { this.#queuedStreamEvents = []; this.#queuedStreamEventIndex = 0; } #compactStreamQueue() { if (this.#queuedStreamEventIndex === this.#queuedStreamEvents.length) { this.#clearStreamQueue(); return; } if (this.#queuedStreamEventIndex >= 1024 && this.#queuedStreamEventIndex * 2 >= this.#queuedStreamEvents.length) { this.#queuedStreamEvents = this.#queuedStreamEvents.slice(this.#queuedStreamEventIndex); this.#queuedStreamEventIndex = 0; } } #detachAbortHandler() { // Clear the indirection first so a retained signal cannot keep this run alive. if (this.#abortHandlerRef) { this.#abortHandlerRef.current = undefined; } if (this.#combinedSignal && this.#abortHandler) { try { this.#combinedSignal.removeEventListener('abort', this.#abortHandler); } catch (err) { (0, logger_1.logModelAndToolActionDebug)(logger_1.default, 'Failed to remove abort listener:', err); } } try { this.#combinedSignalCleanup(); } catch (err) { (0, logger_1.logModelAndToolActionDebug)(logger_1.default, 'Failed to clean up combined abort listeners:', err); } this.#combinedSignalCleanup = () => { }; this.#abortHandler = undefined; this.#abortHandlerRef = undefined; } } exports.StreamedRunResult = StreamedRunResult; //# sourceMappingURL=result.js.map