@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
234 lines • 8.58 kB
JavaScript
import { AgentHooks } from "./lifecycle.mjs";
import { getAllMcpTools } from "./mcp.mjs";
import { tool, } from "./tool.mjs";
import { Runner } from "./run.mjs";
import { toFunctionToolName } from "./utils/tools.mjs";
import { getOutputText } from "./utils/messages.mjs";
import { isAgentToolInput } from "./utils/typeGuards.mjs";
import { isZodObject } from "./utils/typeGuards.mjs";
import { ModelBehaviorError, UserError } from "./errors.mjs";
import logger from "./logger.mjs";
/**
* The class representing an AI agent configured with instructions, tools, guardrails, handoffs and more.
*
* We strongly recommend passing `instructions`, which is the "system prompt" for the agent. In
* addition, you can pass `handoffDescription`, which is a human-readable description of the
* agent, used when the agent is used inside tools/handoffs.
*
* Agents are generic on the context type. The context is a (mutable) object you create. It is
* passed to tool functions, handoffs, guardrails, etc.
*/
export class Agent extends AgentHooks {
/**
* Create an Agent with handoffs and automatically infer the union type for TOutput from the handoff agents' output types.
*/
static create(config) {
return new Agent({
...config,
handoffs: config.handoffs,
outputType: config.outputType,
handoffOutputTypeWarningEnabled: false,
});
}
static DEFAULT_MODEL_PLACEHOLDER = '';
name;
instructions;
prompt;
handoffDescription;
handoffs;
model;
modelSettings;
tools;
mcpServers;
inputGuardrails;
outputGuardrails;
outputType = 'text';
toolUseBehavior;
resetToolChoice;
constructor(config) {
super();
if (typeof config.name !== 'string' || config.name.trim() === '') {
throw new UserError('Agent must have a name.');
}
this.name = config.name;
this.instructions = config.instructions ?? Agent.DEFAULT_MODEL_PLACEHOLDER;
this.prompt = config.prompt;
this.handoffDescription = config.handoffDescription ?? '';
this.handoffs = config.handoffs ?? [];
this.model = config.model ?? '';
this.modelSettings = config.modelSettings ?? {};
this.tools = config.tools ?? [];
this.mcpServers = config.mcpServers ?? [];
this.inputGuardrails = config.inputGuardrails ?? [];
this.outputGuardrails = config.outputGuardrails ?? [];
if (config.outputType) {
this.outputType = config.outputType;
}
this.toolUseBehavior = config.toolUseBehavior ?? 'run_llm_again';
this.resetToolChoice = config.resetToolChoice ?? true;
// --- Runtime warning for handoff output type compatibility ---
if (config.handoffOutputTypeWarningEnabled === undefined ||
config.handoffOutputTypeWarningEnabled) {
if (this.handoffs && this.outputType) {
const outputTypes = new Set([JSON.stringify(this.outputType)]);
for (const h of this.handoffs) {
if ('outputType' in h && h.outputType) {
outputTypes.add(JSON.stringify(h.outputType));
}
else if ('agent' in h && h.agent.outputType) {
outputTypes.add(JSON.stringify(h.agent.outputType));
}
}
if (outputTypes.size > 1) {
logger.warn(`[Agent] Warning: Handoff agents have different output types: ${Array.from(outputTypes).join(', ')}. You can make it type-safe by using Agent.create({ ... }) method instead.`);
}
}
}
}
/**
* Output schema name.
*/
get outputSchemaName() {
if (this.outputType === 'text') {
return 'text';
}
else if (isZodObject(this.outputType)) {
return 'ZodOutput';
}
else if (typeof this.outputType === 'object') {
return this.outputType.name;
}
throw new Error(`Unknown output type: ${this.outputType}`);
}
/**
* Makes a copy of the agent, with the given arguments changed. For example, you could do:
*
* ```
* const newAgent = agent.clone({ instructions: 'New instructions' })
* ```
*
* @param config - A partial configuration to change.
* @returns A new agent with the given changes.
*/
clone(config) {
return new Agent({
...this,
...config,
});
}
/**
* Transform this agent into a tool, callable by other agents.
*
* This is different from handoffs in two ways:
* 1. In handoffs, the new agent receives the conversation history. In this tool, the new agent
* receives generated input.
* 2. In handoffs, the new agent takes over the conversation. In this tool, the new agent is
* called as a tool, and the conversation is continued by the original agent.
*
* @param options - Options for the tool.
* @returns A tool that runs the agent and returns the output text.
*/
asTool(options) {
const { toolName, toolDescription, customOutputExtractor } = options;
return tool({
name: toolName ?? toFunctionToolName(this.name),
description: toolDescription ?? '',
parameters: {
type: 'object',
properties: {
input: {
type: 'string',
},
},
required: ['input'],
additionalProperties: false,
},
strict: true,
execute: async (data, context) => {
if (!isAgentToolInput(data)) {
throw new ModelBehaviorError('Agent tool called with invalid input');
}
const runner = new Runner();
const result = await runner.run(this, data.input, {
context: context?.context,
});
if (typeof customOutputExtractor === 'function') {
return customOutputExtractor(result);
}
return getOutputText(result.rawResponses[result.rawResponses.length - 1]);
},
});
}
/**
* Returns the system prompt for the agent.
*
* If the agent has a function as its instructions, this function will be called with the
* runContext and the agent instance.
*/
async getSystemPrompt(runContext) {
if (typeof this.instructions === 'function') {
return await this.instructions(runContext, this);
}
return this.instructions;
}
/**
* Returns the prompt template for the agent, if defined.
*
* If the agent has a function as its prompt, this function will be called with the
* runContext and the agent instance.
*/
async getPrompt(runContext) {
if (typeof this.prompt === 'function') {
return await this.prompt(runContext, this);
}
return this.prompt;
}
/**
* Fetches the available tools from the MCP servers.
* @returns the MCP powered tools
*/
async getMcpTools(runContext) {
if (this.mcpServers.length > 0) {
return getAllMcpTools(this.mcpServers, runContext, this, false);
}
return [];
}
/**
* ALl agent tools, including the MCPl and function tools.
*
* @returns all configured tools
*/
async getAllTools(runContext) {
return [...(await this.getMcpTools(runContext)), ...this.tools];
}
/**
* Processes the final output of the agent.
*
* @param output - The output of the agent.
* @returns The parsed out.
*/
processFinalOutput(output) {
if (this.outputType === 'text') {
return output;
}
if (typeof this.outputType === 'object') {
const parsed = JSON.parse(output);
if (isZodObject(this.outputType)) {
return this.outputType.parse(parsed);
}
return parsed;
}
throw new Error(`Unknown output type: ${this.outputType}`);
}
/**
* Returns a JSON representation of the agent, which is serializable.
*
* @returns A JSON object containing the agent's name.
*/
toJSON() {
return {
name: this.name,
};
}
}
//# sourceMappingURL=agent.mjs.map