UNPKG

@openai/agents-core

Version:

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

604 lines • 26.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isDeferredHostedMcpTool = isDeferredHostedMcpTool; exports.addLoadedToolNamesFromToolSearchOutput = addLoadedToolNamesFromToolSearchOutput; exports.recordHostedMcpDiscovery = recordHostedMcpDiscovery; exports.toolSearchOutputLoadsHostedMcpTool = toolSearchOutputLoadsHostedMcpTool; exports.addHostedMcpToolsFromToolSearchOutput = addHostedMcpToolsFromToolSearchOutput; exports.validateClientToolSearchSupport = validateClientToolSearchSupport; exports.resolveBuiltInClientToolSearchTools = resolveBuiltInClientToolSearchTools; exports.createClientToolSearchOutputFromTools = createClientToolSearchOutputFromTools; exports.createBuiltInClientToolSearchOutput = createBuiltInClientToolSearchOutput; exports.executeCustomClientToolSearch = executeCustomClientToolSearch; exports.filterEnabledToolSearchRuntimeTools = filterEnabledToolSearchRuntimeTools; exports.registerRuntimeToolSearchTools = registerRuntimeToolSearchTools; exports.getClientToolSearchHelper = getClientToolSearchHelper; 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 mcpApproval_1 = require("../utils/mcpApproval.js"); const toolSearch_1 = require("../utils/toolSearch.js"); function isDeferredFunctionTool(tool) { return tool.type === 'function' && tool.deferLoading === true; } function isTopLevelDeferredFunctionTool(tool) { return (isDeferredFunctionTool(tool) && !(0, toolIdentity_1.getExplicitFunctionToolNamespace)(tool)); } function isDeferredHostedMcpTool(tool) { return (tool.type === 'hosted_tool' && tool.providerData?.type === 'mcp' && tool.providerData.defer_loading === true); } function getBuiltInClientToolSearchArguments(value) { if (!value || typeof value !== 'object') { return undefined; } const paths = value.paths; if (!Array.isArray(paths)) { return undefined; } return { paths: paths.filter((path) => typeof path === 'string' && path.length > 0), }; } function getDeferredFunctionToolsByName(tools) { return new Map(tools .filter(isTopLevelDeferredFunctionTool) .map((tool) => [tool.name, tool])); } function getDeferredNamespaceToolsByName(tools) { const namespaces = new Map(); for (const tool of tools) { if (!isDeferredFunctionTool(tool)) { continue; } const namespace = (0, toolIdentity_1.getExplicitFunctionToolNamespace)(tool); if (!namespace) { continue; } const description = (0, toolIdentity_1.getFunctionToolNamespaceDescription)(tool); if (!description) { throw new errors_1.UserError(`Deferred namespace "${namespace}" must provide a description for built-in client tool_search.`); } const existing = namespaces.get(namespace); if (!existing) { namespaces.set(namespace, { description, tools: [tool] }); continue; } existing.tools.push(tool); } return namespaces; } function getDeferredHostedMcpToolsByServerLabel(tools) { return new Map(tools .filter(isDeferredHostedMcpTool) .map((tool) => [tool.providerData.server_label, tool])); } function getDeferredNamespacedFunctionToolsByQualifiedName(tools) { const qualifiedTools = new Map(); for (const tool of tools) { if (!isDeferredFunctionTool(tool)) { continue; } const namespace = (0, toolIdentity_1.getExplicitFunctionToolNamespace)(tool); if (!namespace) { continue; } const description = (0, toolIdentity_1.getFunctionToolNamespaceDescription)(tool); const qualifiedName = (0, toolIdentity_1.getFunctionToolQualifiedName)(tool); if (!description || !qualifiedName) { continue; } qualifiedTools.set(qualifiedName, { namespace, description, tool }); } return qualifiedTools; } function serializeFunctionToolForToolSearchOutput(tool) { const serialized = (0, serialize_1.serializeTool)(tool); const namespace = (0, toolIdentity_1.getExplicitFunctionToolNamespace)(tool); const { providerData: _providerData, ...toolSearchPayload } = serialized; void _providerData; if (!namespace) { return toolSearchPayload; } const { namespace: _namespace, namespaceDescription, ...toolPayload } = toolSearchPayload; void namespaceDescription; return toolPayload; } function serializeHostedMcpToolForToolSearchOutput(tool) { const { on_approval: _onApproval, ...providerData } = tool.providerData; void _onApproval; return providerData; } function appendNamespaceMemberToolSearchOutput(args) { const { namespace, description, tool, namespaceOutputIndexByName, resolvedTools, } = args; const existingIndex = namespaceOutputIndexByName.get(namespace); const serializedTool = serializeFunctionToolForToolSearchOutput(tool); if (typeof existingIndex === 'number') { const existingNamespace = resolvedTools[existingIndex]; if (existingNamespace?.type === 'namespace' && Array.isArray(existingNamespace.tools)) { existingNamespace.tools.push(serializedTool); return; } } namespaceOutputIndexByName.set(namespace, resolvedTools.length); resolvedTools.push({ type: 'namespace', name: namespace, description, tools: [serializedTool], }); } function isClientToolSearchToolWithCustomParameters(tool) { return (tool.type === 'hosted_tool' && tool.providerData?.type === 'tool_search' && tool.providerData.execution === 'client' && tool.providerData.parameters != null); } function isClientToolSearchTool(tool) { return (tool.type === 'hosted_tool' && tool.providerData?.type === 'tool_search' && tool.providerData.execution === 'client'); } function normalizeClientToolSearchExecutorResult(result) { if (typeof result === 'undefined' || result === null) { return []; } return Array.isArray(result) ? result : [result]; } function addLoadedToolName(loadedToolNames, name, namespace) { if (typeof name !== 'string' || name.length === 0) { return; } const explicitNamespace = typeof namespace === 'string' && namespace.length > 0 ? namespace : undefined; const qualifiedName = (0, toolIdentity_1.toolQualifiedName)(name, explicitNamespace); if (qualifiedName) { loadedToolNames.add(qualifiedName); } if (!explicitNamespace || explicitNamespace === name) { loadedToolNames.add(name); } } function collectLoadedToolNamesFromSearchResult(searchResult, loadedToolNames, namespace, includeMcpDescriptors = true) { if (!searchResult || typeof searchResult !== 'object') { return; } const candidate = searchResult; if (candidate.type === 'tool_reference') { addLoadedToolName(loadedToolNames, candidate.functionName, candidate.namespace ?? namespace); return; } if (candidate.type === 'function') { addLoadedToolName(loadedToolNames, candidate.name, candidate.namespace ?? namespace); return; } const mcpProviderData = getHostedMcpProviderDataFromSearchResult(candidate); if (mcpProviderData) { if (includeMcpDescriptors) { addLoadedToolName(loadedToolNames, mcpProviderData.server_label); } return; } if (candidate.type === 'namespace' && Array.isArray(candidate.tools)) { const nestedNamespace = typeof candidate.name === 'string' && candidate.name.length > 0 ? candidate.name : namespace; for (const nestedTool of candidate.tools) { collectLoadedToolNamesFromSearchResult(nestedTool, loadedToolNames, nestedNamespace, includeMcpDescriptors); } } } function normalizeStringRecord(value) { if (!value || typeof value !== 'object') { return undefined; } const entries = Object.entries(value).filter(([key, entryValue]) => typeof key === 'string' && typeof entryValue === 'string'); if (entries.length === 0) { return undefined; } return Object.fromEntries(entries); } function normalizeHostedMcpProviderData(value) { if (!value || typeof value !== 'object') { return undefined; } const candidate = value; if (candidate.type !== 'mcp' || typeof candidate.server_label !== 'string' || candidate.server_label.length === 0) { return undefined; } const normalized = { type: 'mcp', server_label: candidate.server_label, require_approval: (0, mcpApproval_1.normalizeHostedMcpRequireApproval)(candidate.require_approval), }; if (typeof candidate.server_url === 'string') { normalized.server_url = candidate.server_url; } if (typeof candidate.connector_id === 'string') { normalized.connector_id = candidate.connector_id; } if (typeof candidate.authorization === 'string') { normalized.authorization = candidate.authorization; } if (candidate.allowed_tools !== undefined) { normalized.allowed_tools = candidate.allowed_tools; } if (Array.isArray(candidate.allowed_callers) && candidate.allowed_callers.every((caller) => caller === 'direct' || caller === 'programmatic')) { normalized.allowed_callers = candidate.allowed_callers; } if (candidate.defer_loading === true) { normalized.defer_loading = true; } const headers = normalizeStringRecord(candidate.headers); if (headers) { normalized.headers = headers; } if (typeof candidate.server_description === 'string') { normalized.server_description = candidate.server_description; } return normalized; } function getHostedMcpProviderDataFromSearchResult(searchResult) { const directProviderData = normalizeHostedMcpProviderData(searchResult); if (directProviderData) { return directProviderData; } if (!searchResult || typeof searchResult !== 'object') { return undefined; } const hostedCandidate = searchResult; if (hostedCandidate.type !== 'hosted_tool') { return undefined; } return normalizeHostedMcpProviderData(hostedCandidate.providerData); } function addLoadedToolNamesFromToolSearchOutput(toolSearchOutput, loadedToolNames) { for (const tool of toolSearchOutput.tools) { collectLoadedToolNamesFromSearchResult(tool, loadedToolNames); } } /** Snapshots namespace discovery before configuration changes can alter its meaning. */ function recordHostedMcpDiscovery(output, tools) { const names = new Set(); if ((0, toolSearch_1.getToolSearchExecution)(output) !== 'client') { for (const result of output.tools) { collectLoadedToolNamesFromSearchResult(result, names, undefined, false); } } const functionNames = new Set(tools .filter((tool) => tool.type === 'function') .flatMap((tool) => [ (0, toolIdentity_1.getFunctionToolQualifiedName)(tool), ...((0, toolIdentity_1.isDeferredTopLevelFunctionTool)(tool) ? [(0, toolIdentity_1.toolQualifiedName)(tool.name, tool.name)] : []), ])); const toolSearchMcpToolNames = [...names].filter((name) => name.startsWith('mcp_') && !functionNames.has(name)); const { toolSearchMcpToolNames: _suppliedProvenance, ...rawOutput } = output; return toolSearchMcpToolNames.length > 0 ? { ...rawOutput, toolSearchMcpToolNames } : rawOutput; } /** Matches explicit MCP discovery or an unambiguous hosted namespace result. */ function toolSearchOutputLoadsHostedMcpTool(output, serverLabel, toolName) { if (output.tools.some((result) => getHostedMcpProviderDataFromSearchResult(result)?.server_label === serverLabel)) { return true; } // Client search namespaces describe application functions, not hosted MCP tools. if ((0, toolSearch_1.getToolSearchExecution)(output) === 'client' || typeof toolName !== 'string') { return false; } const qualifiedName = (0, toolIdentity_1.toolQualifiedName)(toolName, `mcp_${serverLabel}`); if (!qualifiedName || !output.toolSearchMcpToolNames?.includes(qualifiedName)) { // Missing provenance cannot be reconstructed from today's tool configuration. // Require fresh discovery rather than reinterpreting historical function results. return false; } const loadedToolNames = new Set(); addLoadedToolNamesFromToolSearchOutput(output, loadedToolNames); return loadedToolNames.has(qualifiedName); } function addHostedMcpToolsFromToolSearchOutput(toolSearchOutput, hostedMcpToolsByServerLabel, options) { const preserveExistingServerLabels = options?.preserveExistingServerLabels; for (const tool of toolSearchOutput.tools) { const providerData = getHostedMcpProviderDataFromSearchResult(tool); if (!providerData) { continue; } if (preserveExistingServerLabels?.has(providerData.server_label) && hostedMcpToolsByServerLabel.has(providerData.server_label)) { continue; } hostedMcpToolsByServerLabel.set(providerData.server_label, { type: 'hosted_tool', name: 'hosted_mcp', providerData, }); } } function validateClientToolSearchSupport(tools) { if (!tools.some((tool) => isClientToolSearchToolWithCustomParameters(tool) && !(0, tool_1.getClientToolSearchExecutor)(tool))) { return; } throw new errors_1.UserError('Runner.run() and Runner.runStreamed() require toolSearchTool({ execution: "client", execute }) when custom client tool_search parameters are provided. Leave parameters unset to use the default built-in { paths: string[] } loader, or use server execution / a custom model loop instead.'); } function resolveBuiltInClientToolSearchTools(paths, tools) { const deferredToolsByName = getDeferredFunctionToolsByName(tools); const deferredNamespaceToolsByName = getDeferredNamespaceToolsByName(tools); const deferredNamespacedToolsByQualifiedName = getDeferredNamespacedFunctionToolsByQualifiedName(tools); const deferredHostedMcpToolsByServerLabel = getDeferredHostedMcpToolsByServerLabel(tools); const ambiguousPath = paths.find((path) => Number(deferredToolsByName.has(path)) + Number(deferredNamespaceToolsByName.has(path)) + Number(deferredNamespacedToolsByQualifiedName.has(path)) + Number(deferredHostedMcpToolsByServerLabel.has(path)) > 1); if (ambiguousPath) { throw new errors_1.UserError(`Runner.run() and Runner.runStreamed() cannot disambiguate built-in client tool_search path "${ambiguousPath}" because it matches multiple deferred tool_search surfaces. Rename one of them or use server tool_search execution.`); } const requestedNamespacePaths = new Set(paths.filter((path) => deferredNamespaceToolsByName.has(path))); const resolvedTools = []; const seenPaths = new Set(); const resolvedQualifiedNames = new Set(); for (const path of paths) { if (seenPaths.has(path)) { continue; } seenPaths.add(path); const tool = deferredToolsByName.get(path); if (!tool) { const namespaceTools = deferredNamespaceToolsByName.get(path); if (namespaceTools) { const unresolvedTools = namespaceTools.tools.filter((tool) => { const qualifiedName = (0, toolIdentity_1.getFunctionToolQualifiedName)(tool); return !qualifiedName || !resolvedQualifiedNames.has(qualifiedName); }); if (unresolvedTools.length === 0) { continue; } for (const namespaceTool of unresolvedTools) { const qualifiedName = (0, toolIdentity_1.getFunctionToolQualifiedName)(namespaceTool); if (qualifiedName) { resolvedQualifiedNames.add(qualifiedName); } } resolvedTools.push(...unresolvedTools); continue; } const namespacedTool = deferredNamespacedToolsByQualifiedName.get(path); if (namespacedTool) { if (requestedNamespacePaths.has(namespacedTool.namespace)) { continue; } if (resolvedQualifiedNames.has(path)) { continue; } resolvedQualifiedNames.add(path); resolvedTools.push(namespacedTool.tool); continue; } } if (tool) { resolvedTools.push(tool); continue; } const hostedMcpTool = deferredHostedMcpToolsByServerLabel.get(path); if (hostedMcpTool) { resolvedTools.push(hostedMcpTool); } } return resolvedTools; } function createClientToolSearchOutputFromTools(toolSearchCall, tools) { const callId = (0, toolSearch_1.resolveToolSearchCallId)(toolSearchCall); const resolvedTools = []; const namespaceOutputIndexByName = new Map(); const seenToolKeys = new Set(); for (const tool of tools) { const runtimeToolKey = (0, tool_1.getToolSearchRuntimeRoutingKey)(tool); if (runtimeToolKey && seenToolKeys.has(runtimeToolKey)) { continue; } if (runtimeToolKey) { seenToolKeys.add(runtimeToolKey); } if (tool.type === 'function') { const namespace = (0, toolIdentity_1.getExplicitFunctionToolNamespace)(tool); if (namespace) { const description = (0, toolIdentity_1.getFunctionToolNamespaceDescription)(tool); if (!description) { throw new errors_1.UserError(`Client tool_search execute() returned namespace member "${namespace}.${tool.name}" without a namespace description.`); } appendNamespaceMemberToolSearchOutput({ namespace, description, tool, namespaceOutputIndexByName, resolvedTools, }); continue; } resolvedTools.push(serializeFunctionToolForToolSearchOutput(tool)); continue; } if (isDeferredHostedMcpTool(tool) || tool.type === 'hosted_tool') { if (tool.type === 'hosted_tool' && tool.providerData?.type === 'mcp') { resolvedTools.push(serializeHostedMcpToolForToolSearchOutput(tool)); continue; } } throw new errors_1.UserError('Client tool_search execute() may only return function tools or hosted MCP tools.'); } return { type: 'tool_search_output', status: 'completed', tools: resolvedTools, providerData: { call_id: callId, execution: 'client', }, }; } function createBuiltInClientToolSearchOutput(toolSearchCall, tools) { const callId = (0, toolSearch_1.resolveToolSearchCallId)(toolSearchCall); const builtInArgs = getBuiltInClientToolSearchArguments(toolSearchCall.arguments); if (!builtInArgs) { throw new errors_1.UserError(`Runner.run() and Runner.runStreamed() only auto-execute built-in client tool_search calls with { paths: string[] }. Custom client tool_search schemas require toolSearchTool({ execution: "client", execute }) or a custom model loop before re-processing call ${callId}.`); } return createClientToolSearchOutputFromTools(toolSearchCall, resolveBuiltInClientToolSearchTools(builtInArgs.paths, tools)); } function indexCustomClientToolSearchRuntimeTools(runtimeTools) { const runtimeToolsByKey = new Map(); for (const runtimeTool of runtimeTools) { if (runtimeTool.type === 'function' && (0, toolIdentity_1.getExplicitFunctionToolNamespace)(runtimeTool) === runtimeTool.name) { throw new errors_1.ModelBehaviorError('Responses tool search reserves same-name namespaces for deferred top-level function tools. Rename the namespace or tool name to avoid ambiguous dispatch.'); } const runtimeToolKey = (0, tool_1.getToolSearchRuntimeRoutingKey)(runtimeTool); if (!runtimeToolKey) { throw new errors_1.ModelBehaviorError('Client tool_search execute() returned an unsupported tool type.'); } if (runtimeToolsByKey.has(runtimeToolKey)) { throw new errors_1.ModelBehaviorError('Client tool_search execute() returned multiple tools with the same routed identity. Return only one tool for each routed identity.'); } runtimeToolsByKey.set(runtimeToolKey, runtimeTool); } return runtimeToolsByKey; } async function executeCustomClientToolSearch(args) { const { agent, runContext, toolSearchCall, toolSearchTool, tools } = args; const executor = (0, tool_1.getClientToolSearchExecutor)(toolSearchTool); if (!executor) { throw new errors_1.UserError('Client tool_search execution requires a registered execute callback.'); } const callbackRuntimeTools = normalizeClientToolSearchExecutorResult(await executor({ agent, availableTools: [...tools], loadDefault: (paths) => resolveBuiltInClientToolSearchTools(paths, tools), runContext, toolCall: toolSearchCall, })); const runtimeTools = await filterEnabledToolSearchRuntimeTools({ agent, runContext, runtimeTools: callbackRuntimeTools, }); indexCustomClientToolSearchRuntimeTools(runtimeTools); return { output: createClientToolSearchOutputFromTools(toolSearchCall, runtimeTools), runtimeTools, callbackRuntimeTools, }; } async function filterEnabledToolSearchRuntimeTools(args) { const { agent, runContext, runtimeTools } = args; const enabledTools = []; for (const runtimeTool of runtimeTools) { if (runtimeTool.type === 'function' && !(await runtimeTool.isEnabled(runContext, agent))) { continue; } enabledTools.push(runtimeTool); } return enabledTools; } function registerRuntimeToolSearchTools(args) { const { availableTools, functionMap, handoffMap, mcpToolMap, replaceableRuntimeToolKeys, replacedRuntimeTools = [], runtimeTools, } = args; const availableToolsByKey = new Map(); for (const tool of availableTools) { const key = (0, tool_1.getToolSearchRuntimeRoutingKey)(tool); if (key) { availableToolsByKey.set(key, tool); } } const runtimeToolsByKey = indexCustomClientToolSearchRuntimeTools(runtimeTools); for (const [runtimeToolKey, runtimeTool] of runtimeToolsByKey) { const existingTool = availableToolsByKey.get(runtimeToolKey); if (existingTool && existingTool !== runtimeTool && !replaceableRuntimeToolKeys?.has(runtimeToolKey)) { throw new errors_1.ModelBehaviorError(logger_1.default.dontLogToolData ? 'Client tool_search execute() returned a tool that conflicts with an existing available tool.' : `Client tool_search execute() returned tool "${(0, tool_1.getToolSearchRuntimeToolKey)(runtimeTool) ?? runtimeToolKey}" that conflicts with an existing available tool.`); } if (runtimeTool.type === 'function') { const bareToolName = (0, toolIdentity_1.getBareTopLevelFunctionToolName)(runtimeTool); if (bareToolName && handoffMap.has(bareToolName)) { throw new errors_1.ModelBehaviorError('Client tool_search execute() returned a bare function tool that conflicts with an available handoff. Assign a unique tool name or use a namespace.'); } continue; } if (runtimeTool.type === 'hosted_tool' && runtimeTool.providerData?.type === 'mcp') { continue; } throw new errors_1.ModelBehaviorError('Client tool_search execute() returned an unsupported tool type.'); } const replacedRuntimeToolSet = new Set(replacedRuntimeTools); for (let index = availableTools.length - 1; index >= 0; index--) { if (replacedRuntimeToolSet.has(availableTools[index])) { availableTools.splice(index, 1); } } for (const [key, tool] of functionMap) { if (replacedRuntimeToolSet.has(tool)) { functionMap.delete(key); } } for (const [serverLabel, tool] of mcpToolMap) { if (replacedRuntimeToolSet.has(tool)) { mcpToolMap.delete(serverLabel); } } const registeredTools = []; for (const [runtimeToolKey, runtimeTool] of runtimeToolsByKey) { const existingIndex = availableTools.findIndex((availableTool) => (0, tool_1.getToolSearchRuntimeRoutingKey)(availableTool) === runtimeToolKey); const existingTool = existingIndex >= 0 ? availableTools[existingIndex] : undefined; if (existingTool === runtimeTool) { if (replaceableRuntimeToolKeys?.has(runtimeToolKey)) { registeredTools.push(runtimeTool); } continue; } if (existingIndex >= 0) { availableTools[existingIndex] = runtimeTool; } else { availableTools.push(runtimeTool); } registeredTools.push(runtimeTool); replaceableRuntimeToolKeys?.add(runtimeToolKey); if (runtimeTool.type === 'function') { const runtimeFunctionMap = (0, toolIdentity_1.buildFunctionToolLookupMap)([runtimeTool]); for (const [key, tool] of runtimeFunctionMap) { functionMap.set(key, tool); } continue; } if (runtimeTool.type === 'hosted_tool' && runtimeTool.providerData?.type === 'mcp') { mcpToolMap.set(runtimeTool.providerData.server_label, runtimeTool); continue; } } return registeredTools; } function getClientToolSearchHelper(tools) { return tools.find(isClientToolSearchTool); } //# sourceMappingURL=toolSearch.js.map