UNPKG

@openai/agents-core

Version:

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

259 lines • 11.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.trackDeferredToolRecovery = trackDeferredToolRecovery; exports.prepareAgentArtifacts = prepareAgentArtifacts; exports.resolveModelVisibleToolNameCollisions = resolveModelVisibleToolNameCollisions; const errors_1 = require("../errors.js"); const logger_1 = __importDefault(require("../logger.js")); const tool_1 = require("../tool.js"); const toolIdentity_1 = require("../toolIdentity.js"); const serialize_1 = require("../utils/serialize.js"); const tracing_1 = require("./tracing.js"); const toolSearch_1 = require("./toolSearch.js"); const computerInitPromisesByRunState = new WeakMap(); // Recovery intent is owned by a live run and its public source Agent, never by // serialized processing metadata or a prepared capability clone. const deferredToolRecoveryAgents = new WeakMap(); function trackDeferredToolRecovery(state, sourceAgent, response) { if (response.functionToolsNotFound?.some((call) => call.reason === 'not_loaded')) { deferredToolRecoveryAgents.set(state, sourceAgent); } else { deferredToolRecoveryAgents.delete(state); } } function getComputerInitMap(state) { let initMap = computerInitPromisesByRunState.get(state); if (!initMap) { initMap = new WeakMap(); computerInitPromisesByRunState.set(state, initMap); } return initMap; } async function initComputerOnce(computer, state) { if (typeof computer.initRun !== 'function') { return; } const initMap = getComputerInitMap(state); const existing = initMap.get(computer); if (existing) { await existing; return; } const initPromise = (async () => { await computer.initRun?.(state._context); })(); initMap.set(computer, initPromise); try { await initPromise; } catch (error) { initMap.delete(computer); throw error; } } /** * Collects tools and handoffs for the current agent so model calls and tracing share the same * snapshot of enabled capabilities. */ async function prepareAgentArtifacts(state, executionAgent = state._currentAgent, toolNameCollisionPolicy = 'warn') { const collectedCapabilities = await collectAgentCapabilities(state, executionAgent); const capabilities = resolveModelVisibleToolNameCollisions(collectedCapabilities.tools, collectedCapabilities.handoffs, toolNameCollisionPolicy); if (deferredToolRecoveryAgents.get(state) === state._currentAgent && !capabilities.tools.some(toolSearch_1.isDeferredHostedMcpTool)) { // A hosted search may omit definitions already loaded in the response chain. // Use the existing local loader for this recovery turn so the active Agent // receives fresh discovery before any deferred function can execute. // Keep deferred MCP on server search: client output serializes its credentials. capabilities.tools = capabilities.tools.map((tool) => tool.type === 'hosted_tool' && tool.providerData?.type === 'tool_search' && tool.providerData.execution !== 'client' && tool.providerData.parameters == null ? { ...tool, providerData: { ...tool.providerData, execution: 'client' }, } : tool); } (0, toolSearch_1.validateClientToolSearchSupport)(capabilities.tools); await warmUpComputerTools(capabilities.tools, state._context); await initializeComputerTools(capabilities.tools, state); state.setCurrentAgentSpan((0, tracing_1.ensureAgentSpan)({ agent: executionAgent, handoffs: capabilities.handoffs, tools: capabilities.tools, currentSpan: state._currentAgentSpan, })); return { ...capabilities, serializedHandoffs: capabilities.handoffs.map((handoff) => (0, serialize_1.serializeHandoff)(handoff)), serializedTools: await Promise.all(capabilities.tools.map(async (tool) => (0, serialize_1.serializeTool)(tool.type === 'computer' ? { ...tool, // Another run may replace tool.computer while preparation awaits. computer: await (0, tool_1.resolveComputer)({ tool, runContext: state._context, }), } : tool))), toolsExplicitlyProvided: executionAgent.hasExplicitToolConfig(), }; } async function collectAgentCapabilities(state, executionAgent) { const handoffs = await executionAgent.getEnabledHandoffs(state._context); const configuredTools = (await executionAgent.getAllTools(state._context, state._currentAgentSpan)); const runtimeLoadedTools = state.getToolSearchRuntimeTools(state._currentAgent); const enabledRuntimeTools = (await Promise.all(runtimeLoadedTools.map(async (tool) => ({ tool, enabled: tool.type !== 'function' || (await tool.isEnabled(state._context, state._currentAgent)), })))) .filter(({ enabled }) => enabled) .map(({ tool }) => tool); return { handoffs, tools: [...configuredTools, ...enabledRuntimeTools] }; } /** @internal */ function resolveModelVisibleToolNameCollisions(tools, handoffs, collisionPolicy) { const topLevelEntriesByName = new Map(); const namespacedFunctionEntriesByName = new Map(); const deferredFunctionEntriesByName = new Map(); const addName = (entriesByName, name, entry) => { const entries = entriesByName.get(name) ?? []; entries.push(entry); entriesByName.set(name, entries); }; for (const [index, tool] of tools.entries()) { if (tool.type === 'function') { const namespace = (0, toolIdentity_1.getFunctionToolNamespace)(tool); if (namespace === tool.name) { throw new errors_1.UserError('Responses tool search reserves same-name namespaces for deferred top-level function tools. Rename the namespace or tool name to avoid ambiguous dispatch.'); } const functionCategory = namespace ? 'namespaced' : tool.deferLoading === true ? 'deferred' : 'top-level'; const entry = { kind: 'function tool', index, functionCategory, }; const dispatchName = (0, toolIdentity_1.getFunctionToolQualifiedName)(tool) ?? tool.name; addName(functionCategory === 'namespaced' ? namespacedFunctionEntriesByName : functionCategory === 'deferred' ? deferredFunctionEntriesByName : topLevelEntriesByName, dispatchName, entry); } } for (const [index, handoff] of handoffs.entries()) { addName(topLevelEntriesByName, handoff.toolName, { kind: 'handoff', index, }); } const namespacedDuplicates = [...namespacedFunctionEntriesByName.entries()] .filter(([, entries]) => entries.length > 1) .sort(([left], [right]) => left.localeCompare(right)); const deferredDuplicates = [...deferredFunctionEntriesByName.entries()] .filter(([, entries]) => entries.length > 1) .sort(([left], [right]) => left.localeCompare(right)); const strictDuplicatesByName = new Map([ ...namespacedDuplicates, ...deferredDuplicates, ]); const strictDuplicates = [...strictDuplicatesByName.entries()].sort(([left], [right]) => left.localeCompare(right)); if (strictDuplicates.length > 0) { throwToolNameCollisionError(strictDuplicates); } const topLevelDuplicates = [...topLevelEntriesByName.entries()] .filter(([, entries]) => entries.length > 1) .sort(([left], [right]) => left.localeCompare(right)); if (topLevelDuplicates.length === 0) { return { tools, handoffs }; } if (collisionPolicy === 'error') { throwToolNameCollisionError(topLevelDuplicates); } const retainedToolIndices = new Set(tools.map((_, index) => index)); const retainedHandoffIndices = new Set(handoffs.map((_, index) => index)); for (const [name, entries] of topLevelDuplicates) { warnToolNameCollision(name, entries); const handoffEntries = entries.filter((entry) => entry.kind === 'handoff'); const winner = handoffEntries.at(-1) ?? entries.at(-1); for (const entry of entries) { if (entry === winner) { continue; } if (entry.kind === 'function tool') { retainedToolIndices.delete(entry.index); } else { retainedHandoffIndices.delete(entry.index); } } } return { tools: tools.filter((_, index) => retainedToolIndices.has(index)), handoffs: handoffs.filter((_, index) => retainedHandoffIndices.has(index)), }; } const toolNameCollisionRemediation = 'Function tools and handoffs must have unique routed names. Assign unique tool names or toolNameOverride values, or use a namespace.'; function throwToolNameCollisionError(duplicates) { if (logger_1.default.dontLogToolData) { throw new errors_1.UserError(`Duplicate enabled function tool or handoff names found. ${toolNameCollisionRemediation}`); } const label = duplicates.length === 1 ? 'name' : 'names'; const details = duplicates .map(([name, entries]) => `'${name}' (${formatToolKinds(entries.map((entry) => entry.kind))})`) .join(', '); throw new errors_1.UserError(`Duplicate enabled tool ${label} found: ${details}. ${toolNameCollisionRemediation}`); } function warnToolNameCollision(name, entries) { if (logger_1.default.dontLogToolData) { logger_1.default.warn('Tool name collision detected. Assign unique routed tool names or enable tool data logging for details. Only the current dispatch winner will be exposed.'); return; } logger_1.default.warn(`Duplicate enabled tool name found: '${name}' (${formatToolKinds(entries.map((entry) => entry.kind))}). ${toolNameCollisionRemediation} Only the current dispatch winner will be exposed.`); } function formatToolKinds(kinds) { const functionToolCount = kinds.filter((kind) => kind === 'function tool').length; const handoffCount = kinds.length - functionToolCount; const descriptions = []; if (functionToolCount > 0) { descriptions.push(functionToolCount === 1 ? 'function tool' : `${functionToolCount} function tools`); } if (handoffCount > 0) { descriptions.push(handoffCount === 1 ? 'handoff' : `${handoffCount} handoffs`); } return descriptions.join(' and '); } async function warmUpComputerTools(tools, runContext) { const computerTools = tools.filter((tool) => tool.type === 'computer'); if (computerTools.length === 0) { return; } await Promise.all(computerTools.map(async (tool) => { await (0, tool_1.resolveComputer)({ tool, runContext }); })); } async function initializeComputerTools(tools, state) { const computerTools = tools.filter((tool) => tool.type === 'computer'); if (computerTools.length === 0) { return; } await Promise.all(computerTools.map(async (tool) => { const computer = await (0, tool_1.resolveComputer)({ tool, runContext: state._context, }); await initComputerOnce(computer, state); })); } //# sourceMappingURL=modelPreparation.js.map