CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langchain

TypeScript framework for building LLM-powered applications with agents, tools, middleware, and model interoperability

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

agents.mddocs/api-reference/

Agent API Reference

Complete API reference for creating and executing LangChain agents.

createAgent

/**
 * Creates a production-ready ReAct (Reasoning + Acting) agent with comprehensive configuration options
 *
 * ReAct agents iteratively reason about tasks and call tools to gather information and take actions.
 * This function creates a fully-configured agent with support for tools, structured outputs, state management,
 * middleware, and more.
 *
 * @template TConfig - Agent type configuration (inferred automatically from params)
 * @param params - Agent configuration parameters. At minimum, must include a model.
 * @param params.model - Language model identifier (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet-20241022")
 *                       or a ChatModel instance for full control. This is effectively required - the agent cannot
 *                       function without a model. String format is "provider:model-name".
 * @param params.llm - Alias for params.model (either can be used)
 * @param params.tools - Array of tools the agent can call, created with tool() function or a configured ToolNode.
 *                       Tools give agents the ability to take actions like searching, calculating, or making API calls.
 * @param params.systemPrompt - System instructions that guide the agent's behavior. Can be a string or SystemMessage.
 *                              Should explain the agent's role, constraints, and available capabilities.
 * @param params.prompt - Alias for params.systemPrompt (either can be used)
 * @param params.responseFormat - Structured output schema using Zod schemas, JSON schemas, or response format strategies.
 *                                When set, the agent will return a validated structuredResponse in addition to messages.
 *                                Supports single schemas, array of schemas (union types), or strategy instances.
 * @param params.stateSchema - Custom state schema for adding persistent state properties beyond built-in state (messages).
 *                             State persists across invocations when using a checkpointer. Can be a Zod object or
 *                             LangGraph AnnotationRoot. Properties from this schema become required/optional parameters
 *                             in agent.invoke() and are accessible in the returned state.
 * @param params.contextSchema - Context schema for read-only data provided per invocation but not persisted.
 *                               Useful for request-scoped data like request IDs, user info, or IP addresses.
 *                               Can be a Zod object or LangGraph AnnotationRoot. Passed via config.context in invoke().
 * @param params.middleware - Array of middleware instances for extending agent behavior with cross-cutting concerns
 *                            like logging, retries, human-in-the-loop, PII detection, etc. Middleware can add hooks,
 *                            state, context, and tools. Executes in array order for lifecycle hooks.
 * @param params.checkpointer - BaseCheckpointSaver instance for persisting agent state across invocations.
 *                              Required for conversation memory and resuming interrupted executions.
 *                              Use thread_id in config to identify separate conversation threads.
 * @param params.store - BaseStore instance for long-term memory storage separate from checkpointer.
 *                       Useful for facts, preferences, or data that outlives conversation threads.
 * @param params.name - Optional agent name for identification in multi-agent systems or logging
 * @param params.description - Optional agent description explaining its purpose or capabilities
 * @param params.includeAgentName - How to expose agent name to the model:
 *                                   - true: Include in all messages
 *                                   - false: Never include (default)
 *                                   - "tool_messages": Include only in tool messages
 * @param params.signal - AbortSignal for cancelling agent execution. Use AbortSignal.timeout(ms) for timeouts.
 * @param params.version - LangGraph version to use ("v1" or "v2"). Defaults to "v2". Usually not needed.
 * @returns ReactAgent instance with methods for execution (invoke, stream, streamEvents, batch)
 *
 * @example Basic agent
 * ```typescript
 * const agent = createAgent({
 *   model: "openai:gpt-4o",
 *   tools: [],
 *   systemPrompt: "You are a helpful assistant.",
 * });
 * ```
 *
 * @example Agent with tools and structured output
 * ```typescript
 * const searchTool = tool(
 *   async ({ query }) => `Results for: ${query}`,
 *   { name: "search", description: "Search", schema: z.object({ query: z.string() }) }
 * );
 *
 * const ResultSchema = z.object({
 *   answer: z.string(),
 *   sources: z.array(z.string()),
 * });
 *
 * const agent = createAgent({
 *   model: "openai:gpt-4o",
 *   tools: [searchTool],
 *   responseFormat: ResultSchema,
 *   systemPrompt: "You are a research assistant.",
 * });
 * ```
 *
 * @example Agent with state and persistence
 * ```typescript
 * import { MemorySaver } from "@langchain/langgraph";
 *
 * const StateSchema = z.object({
 *   userId: z.string(),
 *   preferences: z.object({ theme: z.string() }).optional(),
 * });
 *
 * const agent = createAgent({
 *   model: "openai:gpt-4o",
 *   tools: [],
 *   stateSchema: StateSchema,
 *   checkpointer: new MemorySaver(),
 * });
 *
 * // First invocation
 * await agent.invoke(
 *   { messages: [{ role: "user", content: "Hi, I'm Alice" }], userId: "user-123" },
 *   { configurable: { thread_id: "thread-1" } }
 * );
 *
 * // Later invocation - state is restored
 * const result = await agent.invoke(
 *   { messages: [{ role: "user", content: "What's my name?" }], userId: "user-123" },
 *   { configurable: { thread_id: "thread-1" } }
 * );
 * ```
 */
function createAgent<TConfig>(params: CreateAgentParams): ReactAgent<TConfig>;

interface CreateAgentParams<
  StructuredResponseFormat = any,
  StateSchema = any,
  ContextSchema = any,
  TResponseFormat = any
> {
  /**
   * Language model as string identifier (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet-20241022")
   * or model instance for full control
   * Required parameter - must be provided
   * Note: "llm" is an alias for "model" (both are accepted)
   */
  model?: string | ChatModel;

  /**
   * Alias for model parameter (either model or llm can be used)
   */
  llm?: string | ChatModel;

  /**
   * Array of tools created with tool() function or configured ToolNode
   */
  tools?: Tool[] | ToolNode;

  /**
   * System instructions as string or SystemMessage
   */
  systemPrompt?: string | SystemMessage;

  /**
   * Alias for systemPrompt parameter
   */
  prompt?: string | SystemMessage;

  /**
   * Structured output schema using Zod schema, JSON schema, or response format strategy
   * When set, agent will return typed structuredResponse in state
   */
  responseFormat?: TResponseFormat;

  /**
   * Custom state schema for memory and additional state properties (persisted)
   * Can be Zod object or LangGraph AnnotationRoot
   */
  stateSchema?: StateSchema;

  /**
   * Context schema for read-only data not persisted across invocations
   * Can be Zod object or LangGraph AnnotationRoot
   */
  contextSchema?: ContextSchema;

  /**
   * Array of middleware instances for extending agent behavior
   */
  middleware?: AgentMiddleware[];

  /**
   * Checkpointer for state persistence across invocations
   */
  checkpointer?: BaseCheckpointSaver;

  /**
   * Long-term memory storage (separate from checkpointer)
   */
  store?: BaseStore;

  /**
   * Agent name for identification
   */
  name?: string;

  /**
   * Agent description
   */
  description?: string;

  /**
   * How to expose agent name to the model
   * - true: Include in all messages
   * - false: Never include
   * - "tool_messages": Include only in tool messages
   */
  includeAgentName?: boolean | "tool_messages";

  /**
   * Abort signal for cancellation
   */
  signal?: AbortSignal;

  /**
   * Graph version to use ("v1" or "v2")
   * @default "v2"
   */
  version?: "v1" | "v2";
}

ReactAgent Class

/**
 * ReAct agent instance with execution methods
 */
class ReactAgent<TConfig> {
  /**
   * Execute agent synchronously and return final state
   * @param input - User input with messages and optional state
   * @param config - Configuration including context and thread_id
   * @returns Promise resolving to final state with messages and structuredResponse
   */
  invoke(
    input: UserInput<TConfig>,
    config?: InvokeConfiguration<TConfig>
  ): Promise<State<TConfig>>;

  /**
   * Stream agent execution with real-time updates
   * @param input - User input with messages and optional state
   * @param config - Stream configuration with streamMode option
   * @returns Async generator yielding state updates
   */
  stream(
    input: UserInput<TConfig>,
    config?: StreamConfiguration<TConfig>
  ): AsyncGenerator<State<TConfig>>;

  /**
   * Stream detailed events during agent execution
   * @param input - User input with messages and optional state
   * @param config - Stream configuration
   * @returns Async generator yielding execution events
   */
  streamEvents(
    input: UserInput<TConfig>,
    config?: StreamConfiguration<TConfig>
  ): AsyncGenerator<StreamEvent>;

  /**
   * Execute agent on multiple inputs in parallel
   * @param inputs - Array of user inputs
   * @param config - Optional batch configuration
   * @returns Promise resolving to array of states
   */
  batch(
    inputs: UserInput<TConfig>[],
    config?: BatchConfiguration<TConfig>
  ): Promise<State<TConfig>[]>;
}

Input Types

/**
 * User input for agent invocation
 */
type UserInput<TStateSchema = any> = {
  /**
   * Array of messages to process
   */
  messages: MessageInput[];
} & Partial<InferSchemaInput<TStateSchema>>;

/**
 * Message input formats
 */
type MessageInput =
  | string // Simple string message (treated as user message)
  | BaseMessage // Full message object
  | {
      role: "user" | "assistant" | "system" | "tool";
      content: string;
      tool_call_id?: string; // Required for tool role
    };

Configuration Types

/**
 * Configuration for agent invocation
 */
interface InvokeConfiguration<ContextSchema = any> {
  /**
   * Read-only context data not persisted across invocations
   */
  context?: InferContextInput<ContextSchema>;

  /**
   * Configurable options including thread_id for persistence
   */
  configurable?: {
    /**
     * Thread ID for state persistence (requires checkpointer)
     */
    thread_id?: string;
    [key: string]: any;
  };

  /**
   * Abort signal for cancellation
   */
  signal?: AbortSignal;
}

/**
 * Configuration for streaming
 */
interface StreamConfiguration<ContextSchema = any> extends InvokeConfiguration<ContextSchema> {
  /**
   * Stream mode
   * - "values": Full state updates
   * - "updates": Only changed values
   * - "messages": Only new messages
   */
  streamMode?: "values" | "updates" | "messages";
}

/**
 * Configuration for batch processing
 */
interface BatchConfiguration<ContextSchema = any> extends InvokeConfiguration<ContextSchema> {
  /**
   * Maximum concurrency for batch processing
   */
  maxConcurrency?: number;
}

State Types

/**
 * Agent state returned from invoke/stream
 */
interface State<TConfig = any> {
  /**
   * Conversation message history
   */
  messages: BaseMessage[];

  /**
   * Structured response when responseFormat is set
   */
  structuredResponse?: InferStructuredResponse<TConfig>;

  /**
   * Additional state properties from stateSchema
   */
  [key: string]: any;
}

/**
 * Built-in state properties
 */
interface BuiltInState {
  /**
   * Message history
   */
  messages: BaseMessage[];

  /**
   * Structured response (when responseFormat is used)
   */
  structuredResponse?: any;
}

Tool Types

/**
 * Information about a tool call
 */
interface ToolCall {
  id: string;
  name: string;
  args: Record<string, any>;
}

/**
 * Information about a tool result
 */
interface ToolResult {
  tool_call_id: string;
  content: string;
  error?: string;
}

/**
 * Executed tool call with result
 */
interface ExecutedToolCall extends ToolCall {
  result: string;
  error?: Error;
}

Interrupt Types

/**
 * Interrupt information from middleware
 */
interface Interrupt<TValue = any> {
  value: TValue;
  resumable: boolean;
  when: "before-tools" | "after-tools" | "before-model" | "after-model";
}

/**
 * Jump targets for controlling execution flow
 */
type JumpToTarget = "model" | "tools" | "end";

Runtime Types

/**
 * Runtime context available to middleware
 */
interface Runtime<TContext = any> {
  /**
   * Read-only context data
   */
  context: TContext;

  /**
   * Configurable options
   */
  configurable: {
    thread_id?: string;
    [key: string]: any;
  };

  /**
   * Additional LangGraph runtime properties
   */
  [key: string]: any;
}

Utility Functions

/**
 * Type guard to check if value is a BaseChatModel
 */
function isBaseChatModel(value: any): value is BaseChatModel;

/**
 * Type guard to check if model is configurable
 */
function isConfigurableModel(value: any): value is ConfigurableModelInterface;

/**
 * Interface for configurable models
 */
interface ConfigurableModelInterface {
  model?: string | ChatModel;
  [key: string]: any;
}

/**
 * Check if tool is a client tool (should run on client)
 */
function isClientTool(tool: Tool): boolean;

/**
 * Validate that LLM has no tools bound
 */
function validateLLMHasNoBoundTools(llm: ChatModel): void;

/**
 * Check if message has tool calls
 */
function hasToolCalls(message: BaseMessage): boolean;

/**
 * Normalize system prompt input
 */
function normalizeSystemPrompt(
  prompt: string | SystemMessage | ((state: State) => string | SystemMessage),
  state?: State
): SystemMessage;

Error Classes

/**
 * Thrown when attempting to bind structured output to a model that already has tools bound
 */
class MultipleToolsBoundError extends Error {
  name: "MultipleToolsBoundError";
  message: "The model already has tools bound to it. Cannot bind structured output format.";
}

/**
 * Thrown when multiple structured outputs are returned but only one was expected
 */
class MultipleStructuredOutputsError extends Error {
  name: "MultipleStructuredOutputsError";
  message: "Multiple structured outputs returned when one was expected";
  outputs: any[];
}

/**
 * Thrown when structured output parsing fails
 */
class StructuredOutputParsingError extends Error {
  name: "StructuredOutputParsingError";
  message: string;
  cause?: Error;
  rawOutput?: string;
}

/**
 * Thrown when tool invocation fails
 */
class ToolInvocationError extends Error {
  name: "ToolInvocationError";
  message: string;
  toolName: string;
  toolInput: any;
  cause?: Error;
}

Agent Name Mode

/**
 * How to include agent name in messages
 */
type AgentNameMode = boolean | "tool_messages";

Type Inference Utilities

Complete type inference system for extracting types from agents at compile time.

Agent Type Inference

/**
 * Complete agent type configuration
 */
type AgentTypeConfig<TResponse, TState, TContext, TMiddleware, TTools> = {
  response: TResponse;
  state: TState;
  context: TContext;
  middleware: TMiddleware;
  tools: TTools;
};

/**
 * Extract any agent type property
 * @template T - Agent type to extract from
 * @template K - Key of AgentTypeConfig to extract
 */
type InferAgentType<T, K extends keyof AgentTypeConfig<any, any, any, any, any>> =
  T extends ReactAgent<infer Config>
    ? Config extends AgentTypeConfig<any, any, any, any, any>
      ? Config[K]
      : never
    : never;

/**
 * Extract response type from agent
 * Returns the inferred type of the structured response
 * @template T - Agent type
 */
type InferAgentResponse<T> = InferAgentType<T, "response">;

/**
 * Extract full merged state from agent (includes built-in state)
 * Merges agent state schema with middleware states and built-in state
 * @template T - Agent type
 */
type InferAgentState<T> = InferAgentType<T, "state">;

/**
 * Extract raw state schema from agent
 * Returns the state schema without merging
 * @template T - Agent type
 */
type InferAgentStateSchema<T> = T extends ReactAgent<infer Config>
  ? Config extends { stateSchema: infer Schema }
    ? Schema
    : undefined
  : never;

/**
 * Extract full merged context from agent
 * Merges agent context schema with middleware contexts
 * @template T - Agent type
 */
type InferAgentContext<T> = InferAgentType<T, "context">;

/**
 * Extract raw context schema from agent
 * Returns the context schema without merging
 * @template T - Agent type
 */
type InferAgentContextSchema<T> = T extends ReactAgent<infer Config>
  ? Config extends { contextSchema: infer Schema }
    ? Schema
    : undefined
  : never;

/**
 * Extract middleware array from agent
 * @template T - Agent type
 */
type InferAgentMiddleware<T> = InferAgentType<T, "middleware">;

/**
 * Extract tools array from agent
 * Includes both agent tools and middleware tools
 * @template T - Agent type
 */
type InferAgentTools<T> = InferAgentType<T, "tools">;

Usage Example:

import { createAgent, type InferAgentState, type InferAgentResponse } from "langchain";
import { z } from "zod";

const StateSchema = z.object({
  userId: z.string(),
  count: z.number(),
});

const ResponseSchema = z.object({
  answer: z.string(),
  confidence: z.number(),
});

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [],
  stateSchema: StateSchema,
  responseFormat: ResponseSchema,
});

// Infer state type (includes built-in state)
type State = InferAgentState<typeof agent>;
// { messages: BaseMessage[]; userId: string; count: number; structuredResponse?: { answer: string; confidence: number } }

// Infer response type
type Response = InferAgentResponse<typeof agent>;
// { answer: string; confidence: number }

// Type-safe access
const result = await agent.invoke({
  messages: [{ role: "user", content: "Hello" }],
  userId: "user123",
  count: 1,
});

const answer: string = result.structuredResponse.answer;
const userId: string = result.userId;

Helper Type Utilities

/**
 * Extract union type from Zod schema array
 * Useful for inferring union response types
 * @template T - Array of Zod schemas
 */
type ExtractZodArrayTypes<T extends readonly any[]> = T extends readonly [
  infer First,
  ...infer Rest
]
  ? z.infer<First> | ExtractZodArrayTypes<Rest>
  : never;

/**
 * Convert tools array to message tool set
 * Creates a mapped type with tool names as keys
 * @template T - Array of tools
 */
type ToolsToMessageToolSet<T extends readonly Tool[]> = {
  [K in T[number]["name"]]: Extract<T[number], { name: K }>;
};

/**
 * Merge agent and middleware tools
 * Combines tools from agent and all middleware
 * @template TAgentTools - Agent's tools array
 * @template TMiddleware - Middleware array
 */
type CombineTools<
  TAgentTools extends readonly Tool[],
  TMiddleware extends readonly AgentMiddleware[]
> = [...TAgentTools, ...InferMiddlewareTools<TMiddleware>];

/**
 * Infer tools from middleware array
 * Recursively extracts tools from all middleware
 * @template T - Middleware array
 */
type InferMiddlewareTools<T extends readonly AgentMiddleware[]> =
  T extends readonly [infer First, ...infer Rest]
    ? First extends AgentMiddleware<any, any, any, infer Tools>
      ? Tools extends readonly Tool[]
        ? [...Tools, ...(Rest extends readonly AgentMiddleware[] ? InferMiddlewareTools<Rest> : [])]
        : Rest extends readonly AgentMiddleware[]
        ? InferMiddlewareTools<Rest>
        : []
      : []
    : [];

/**
 * Infer tools array from middleware array
 * @template T - Middleware array or other type
 */
type InferMiddlewareToolsArray<T> = T extends readonly AgentMiddleware[]
  ? InferMiddlewareTools<T>
  : [];

/**
 * Check if all properties are optional
 * Used for context optionality checking
 * @template T - Object type to check
 */
type IsAllOptional<T> = {} extends Required<T> ? true : false;

/**
 * Context optionality helper
 * Makes context optional if all properties are optional
 * @template TContext - Context type
 */
type WithMaybeContext<TContext> = IsAllOptional<TContext> extends true
  ? { context?: TContext }
  : { context: TContext };

/**
 * Infer input type from schema
 * Handles both Zod schemas and plain objects
 * @template T - Schema type
 */
type InferSchemaInput<T> = T extends z.ZodType<infer Output, any, infer Input>
  ? Input
  : T extends { [key: string]: any }
  ? T
  : {};

/**
 * Infer context input type
 * Handles both Zod schemas and plain objects
 * @template ContextSchema - Context schema type
 */
type InferContextInput<ContextSchema> = ContextSchema extends z.ZodType<
  any,
  any,
  infer Input
>
  ? Input
  : ContextSchema extends { [key: string]: any }
  ? ContextSchema
  : {};

/**
 * Resolve agent type configuration
 * Maps CreateAgentParams to AgentTypeConfig
 * @template T - CreateAgentParams type
 */
type ResolveAgentTypeConfig<T> = T extends CreateAgentParams<
  infer Response,
  infer State,
  infer Context,
  infer ResponseFormat
>
  ? AgentTypeConfig<Response, State, Context, any, any>
  : DefaultAgentTypeConfig;

/**
 * Default agent type configuration
 * Used when no custom types are specified
 */
type DefaultAgentTypeConfig = AgentTypeConfig<
  undefined,
  Record<string, never>,
  Record<string, never>,
  readonly AgentMiddleware[],
  readonly Tool[]
>;

/**
 * Any annotation root type
 * Placeholder for LangGraph annotation roots
 */
type AnyAnnotationRoot = {
  [key: string]: any;
};

/**
 * Normalized schema input
 * Extracts input type from Zod schema or returns schema as-is
 * @template TSchema - Schema type
 */
type NormalizedSchemaInput<TSchema> = TSchema extends z.ZodType<
  any,
  any,
  infer Input
>
  ? Input
  : TSchema;

Advanced Type Inference Examples:

// Union response types
const EmailSchema = z.object({ type: z.literal("email"), to: z.string() });
const TaskSchema = z.object({ type: z.literal("task"), title: z.string() });

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [],
  responseFormat: [EmailSchema, TaskSchema],
});

type Response = InferAgentResponse<typeof agent>;
// { type: "email", to: string } | { type: "task", title: string }

// Tool name extraction
const searchTool = tool(async ({ query }) => "...", {
  name: "search",
  description: "Search",
  schema: z.object({ query: z.string() }),
});

const tools = [searchTool] as const;
type ToolNames = typeof tools[number]["name"];
// "search"

// Tools to message set
type ToolSet = ToolsToMessageToolSet<typeof tools>;
// { search: typeof searchTool }

See Type Inference Guide for complete documentation and examples.

Utility Functions

Model Utilities

/**
 * Type guard to check if value is a BaseChatModel
 * @param value - Value to check
 * @returns True if value is a BaseChatModel instance
 */
function isBaseChatModel(value: any): value is BaseChatModel;

/**
 * Type guard to check if model is configurable
 * @param value - Value to check
 * @returns True if value implements ConfigurableModelInterface
 */
function isConfigurableModel(value: any): value is ConfigurableModelInterface;

/**
 * Interface for configurable models
 */
interface ConfigurableModelInterface {
  model?: string | ChatModel;
  [key: string]: any;
}

Tool Utilities

/**
 * Check if tool is a client tool (should run on client)
 * @param tool - Tool to check
 * @returns True if tool should run on client side
 */
function isClientTool(tool: Tool): boolean;

/**
 * Validate that LLM has no tools bound
 * Throws error if tools are already bound to prevent conflicts
 * @param llm - Chat model to validate
 * @throws MultipleToolsBoundError if tools already bound
 */
function validateLLMHasNoBoundTools(llm: ChatModel): void;

/**
 * Check if message has tool calls
 * @param message - Message to check
 * @returns True if message contains tool calls
 */
function hasToolCalls(message: BaseMessage): boolean;

System Prompt Utilities

/**
 * Normalize system prompt input to SystemMessage
 * @param prompt - System prompt as string, SystemMessage, or function
 * @param state - Optional state for function prompts
 * @returns Normalized SystemMessage
 */
function normalizeSystemPrompt(
  prompt: string | SystemMessage | ((state: State) => string | SystemMessage),
  state?: State
): SystemMessage;

Runtime Types

/**
 * Runtime context available to middleware and agent internals
 */
interface Runtime<TContext = any> {
  /**
   * Read-only context data
   */
  context: TContext;

  /**
   * Configurable options including thread_id
   */
  configurable: {
    thread_id?: string;
    [key: string]: any;
  };

  /**
   * Additional LangGraph runtime properties
   */
  [key: string]: any;
}

Advanced Agent Behaviors

Execution Flow Details

The agent execution follows this precise flow:

  1. Initialization Phase

    • Validate model configuration
    • Check for conflicting configurations (tools + structured output binding)
    • Merge state/context schemas from agent and middleware
    • Compile middleware hooks in order
    • Build tool node with all tools (agent + middleware)
  2. Pre-Execution Phase (per invoke/stream)

    • Execute beforeAgent hooks from all middleware (in array order)
    • Validate input against state schema
    • Normalize message inputs to BaseMessage instances
    • Apply context from config to runtime
  3. Execution Loop (ReAct pattern)

    • Execute beforeModel hooks from all middleware
    • Execute wrapModelCall hooks (innermost to outermost)
    • Call language model with current messages
    • Execute afterModel hooks from all middleware
    • If model returns tool calls:
      • Execute wrapToolCall hooks for each tool (innermost to outermost)
      • Execute tools (parallel or sequential based on configuration)
      • Add tool results to messages
      • Loop back to step 1 (model call)
    • If model returns final response:
      • Extract structured response if responseFormat is set
      • Proceed to post-execution
  4. Post-Execution Phase

    • Execute afterAgent hooks from all middleware (in array order)
    • Validate structured response against schema
    • Save checkpoint if checkpointer is configured
    • Return final state

State Management Details

State Persistence:

  • State is persisted only when checkpointer is configured
  • Requires thread_id in config.configurable
  • State is loaded before execution and saved after completion
  • Failed executions do not save state (atomic updates)

State Merging Order:

  1. Built-in state (messages, structuredResponse)
  2. Agent state schema properties
  3. Middleware state schema properties (merged in middleware array order)
  4. Later middleware can override earlier middleware state

State Schema Validation:

  • Input is validated against merged state schema before execution
  • Validation uses Zod's parse/safeParse based on schema type
  • Invalid input throws validation error with details
  • Optional properties with defaults are automatically populated

Context vs State

Context (read-only, per-invocation):

  • Passed via config.context in invoke/stream calls
  • Available to middleware hooks via runtime.context
  • Not persisted across invocations
  • Useful for request-scoped data (request IDs, IP addresses, auth tokens)
  • Merged from agent context schema + middleware context schemas

State (persisted, mutable):

  • Passed via input properties in invoke/stream calls
  • Available to middleware hooks via state parameter
  • Persisted when checkpointer is configured
  • Useful for conversation history, user preferences, session data
  • Merged from agent state schema + middleware state schemas

Tool Execution Details

Tool Invocation:

  • Tools are called based on model's tool_calls in AIMessage
  • Multiple tool calls can execute in parallel (default behavior)
  • Tool errors are caught and returned as error messages
  • Failed tools don't halt execution (agent can retry/continue)

Tool Call Limiting:

  • No built-in limit on tool calls per invocation
  • Use toolCallLimitMiddleware to enforce limits
  • Infinite loops are possible if agent keeps calling tools
  • Consider implementing max_iterations logic in middleware

Tool Arguments:

  • Arguments are validated against tool schema before invocation
  • Invalid arguments result in tool error message
  • Model receives error message and can retry with corrected args
  • Type coercion follows Zod schema coercion rules

Structured Output Details

Response Format Strategies:

  • Direct schema: Model's native structured output (provider-specific)
  • Tool strategy: Wraps schema as a tool call (universal compatibility)
  • Provider strategy: Uses provider-specific structured output API
  • JSON schema: Direct JSON schema format (some providers only)

Extraction Process:

  1. Model generates response (varies by strategy)
  2. Extract structured data from response
  3. Validate against schema (Zod parse/safeParse)
  4. Set structuredResponse in state
  5. Validation failure throws StructuredOutputParsingError

Union Type Handling:

  • Array of schemas creates discriminated union
  • Model chooses which schema to return
  • Validation tries each schema in order
  • First successful parse is returned
  • All schemas fail → StructuredOutputParsingError

Message Processing Details

Message Normalization:

  • String → HumanMessage(content)
  • {role, content} → Corresponding message type
  • BaseMessage → Pass through unchanged
  • Invalid format → Validation error

Message History Management:

  • Messages accumulate in state.messages array
  • No automatic trimming/summarization (use middleware)
  • Large message histories can exceed token limits
  • Use trimMessages or summarizationMiddleware for long conversations

Tool Messages:

  • Automatically created from tool execution results
  • Include tool_call_id linking to originating tool call
  • Error messages include error field
  • Model receives both successful and failed tool results

Middleware Composition

Hook Execution Order:

  • beforeAgent: Middleware array order (0 → N)
  • beforeModel: Middleware array order (0 → N)
  • afterModel: Middleware array order (0 → N)
  • afterAgent: Middleware array order (0 → N)
  • wrapToolCall: Nested (innermost middleware wraps outermost)
  • wrapModelCall: Nested (innermost middleware wraps outermost)

State/Context Merging:

  • Middleware state schemas merge left-to-right
  • Later middleware can override earlier middleware properties
  • Conflicts are resolved by taking rightmost value
  • Context follows same merging rules

Tool Merging:

  • Agent tools + middleware tools (concatenated)
  • Later middleware tools added to end
  • Name conflicts are not detected (first match wins)
  • Use unique tool names across all middleware

Performance Considerations

Token Usage:

  • Every tool call adds messages (tool call + result)
  • System prompts included in every model call
  • Large state schemas don't directly affect tokens (not sent to model)
  • Use summarizationMiddleware to manage conversation length

Latency:

  • Sequential tool calls add latency (one round-trip per call)
  • Parallel tool calls execute concurrently (limited by provider)
  • Middleware hooks add minimal overhead (< 1ms typically)
  • Streaming reduces perceived latency for long responses

Memory:

  • Message history grows unbounded without trimming
  • State persisted in checkpointer (memory/disk depends on implementation)
  • Large structured outputs consume memory
  • Consider streaming for memory-constrained environments

Concurrency:

  • Single agent instance is thread-safe for reads
  • Multiple concurrent invocations supported
  • State isolation via thread_id
  • Tool execution can be parallel or sequential

Error Recovery

Automatic Recovery:

  • Tool errors: Agent can retry with different args
  • Model errors: No automatic retry (use modelRetryMiddleware)
  • Parse errors: No automatic retry (use retry middleware)
  • Network errors: No automatic retry (use retry middleware)

Manual Recovery:

  • Catch errors in application code
  • Inspect error details (type, cause, tool name)
  • Retry with modified input
  • Resume from checkpoint if using checkpointer

Error Propagation:

  • Tool errors → Returned as tool error messages (execution continues)
  • Model errors → Thrown immediately (execution halts)
  • Validation errors → Thrown immediately (execution halts)
  • Middleware errors → Thrown immediately (execution halts)

Internal Implementation Notes

For Advanced Users:

The agent is implemented as a LangGraph StateGraph with nodes:

  • __start__: Entry point
  • agent: Model invocation node
  • tools: Tool execution node
  • __end__: Exit point

Edges:

  • __start__agent
  • agenttools (if tool calls present)
  • agent__end__ (if no tool calls)
  • toolsagent (loop back after tool execution)

Middleware is implemented as graph transforms that:

  • Add state channels for middleware state/context
  • Wrap nodes with before/after hooks
  • Intercept tool/model calls with wrapper functions

This architecture allows:

  • Resumable execution (via checkpointer)
  • Human-in-the-loop interrupts (middleware can raise interrupts)
  • Custom state management (arbitrary state properties)
  • Tool streaming (events emitted during execution)

Related Documentation

docs

glossary.md

index.md

quick-reference.md

task-index.md

tile.json