docs
Complete type inference utilities for extracting types from agents, middleware, and schemas.
/**
* 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
*/
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
*/
type InferAgentResponse<T> = InferAgentType<T, "response">;
/**
* Extract full merged state from agent (includes built-in state)
*/
type InferAgentState<T> = InferAgentType<T, "state">;
/**
* Extract raw state schema from agent
*/
type InferAgentStateSchema<T> = T extends ReactAgent<infer Config>
? Config extends { stateSchema: infer Schema }
? Schema
: undefined
: never;
/**
* Extract full merged context from agent
*/
type InferAgentContext<T> = InferAgentType<T, "context">;
/**
* Extract raw context schema from agent
*/
type InferAgentContextSchema<T> = T extends ReactAgent<infer Config>
? Config extends { contextSchema: infer Schema }
? Schema
: undefined
: never;
/**
* Extract middleware array from agent
*/
type InferAgentMiddleware<T> = InferAgentType<T, "middleware">;
/**
* Extract tools array from agent
*/
type InferAgentTools<T> = InferAgentType<T, "tools">;/**
* Complete middleware type configuration
*/
type MiddlewareTypeConfig<TSchema, TContextSchema, TFullContext, TTools> = {
schema: TSchema;
contextSchema: TContextSchema;
fullContext: TFullContext;
tools: TTools;
};
/**
* Extract middleware schema
*/
type InferMiddlewareSchema<T> = T extends AgentMiddleware<
infer Schema,
any,
any,
any
>
? Schema
: never;
/**
* Extract middleware state
*/
type InferMiddlewareState<T> = T extends AgentMiddleware<
infer Schema,
any,
any,
any
>
? InferSchemaInput<Schema>
: never;
/**
* Extract middleware input state
*/
type InferMiddlewareInputState<T> = T extends AgentMiddleware<
infer Schema,
any,
any,
any
>
? Schema extends z.ZodType<any, any, infer Input>
? Input
: Schema
: never;
/**
* Merge states from middleware array
*/
type InferMiddlewareStates<T extends readonly AgentMiddleware[]> =
T extends readonly [infer First, ...infer Rest]
? First extends AgentMiddleware
? InferMiddlewareState<First> &
(Rest extends readonly AgentMiddleware[]
? InferMiddlewareStates<Rest>
: {})
: {}
: {};
/**
* Extract middleware context
*/
type InferMiddlewareContext<T> = T extends AgentMiddleware<
any,
infer ContextSchema,
any,
any
>
? InferSchemaInput<ContextSchema>
: never;
/**
* Merge contexts from middleware array
*/
type InferMiddlewareContexts<T extends readonly AgentMiddleware[]> =
T extends readonly [infer First, ...infer Rest]
? First extends AgentMiddleware
? InferMiddlewareContext<First> &
(Rest extends readonly AgentMiddleware[]
? InferMiddlewareContexts<Rest>
: {})
: {}
: {};
/**
* Full merged state with built-in state
*/
type InferMergedState<T> = BuiltInState & InferMiddlewareStates<T>;/**
* Infer schema input type
*/
type InferSchemaInput<A> = A extends z.ZodType<any, any, infer Input>
? Input
: A extends { [key: string]: any }
? A
: {};
/**
* Infer context input type
*/
type InferContextInput<ContextSchema> = ContextSchema extends z.ZodType<
any,
any,
infer Input
>
? Input
: ContextSchema extends { [key: string]: any }
? ContextSchema
: {};
/**
* Resolve agent type configuration
*/
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
*/
type DefaultAgentTypeConfig = AgentTypeConfig<
undefined,
Record<string, never>,
Record<string, never>,
readonly AgentMiddleware[],
readonly Tool[]
>;/**
* Extract union type from Zod schema array
*/
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
*/
type ToolsToMessageToolSet<T extends readonly Tool[]> = {
[K in T[number]["name"]]: Extract<T[number], { name: K }>;
};
/**
* Merge agent and middleware tools
*/
type CombineTools<
TAgentTools extends readonly Tool[],
TMiddleware extends readonly AgentMiddleware[]
> = [...TAgentTools, ...InferMiddlewareTools<TMiddleware>];
/**
* Infer tools from middleware
*/
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
*/
type InferMiddlewareToolsArray<T> = T extends readonly AgentMiddleware[]
? InferMiddlewareTools<T>
: [];Complete type system for extracting types from middleware.
/**
* Complete middleware type configuration
*/
type MiddlewareTypeConfig<TSchema, TContextSchema, TFullContext, TTools> = {
schema: TSchema;
contextSchema: TContextSchema;
fullContext: TFullContext;
tools: TTools;
};
/**
* Extract middleware schema
* @template T - Middleware type
*/
type InferMiddlewareSchema<T> = T extends AgentMiddleware<
infer Schema,
any,
any,
any
>
? Schema
: never;
/**
* Extract middleware state
* @template T - Middleware type
*/
type InferMiddlewareState<T> = T extends AgentMiddleware<
infer Schema,
any,
any,
any
>
? InferSchemaInput<Schema>
: never;
/**
* Extract middleware input state
* @template T - Middleware type
*/
type InferMiddlewareInputState<T> = T extends AgentMiddleware<
infer Schema,
any,
any,
any
>
? Schema extends z.ZodType<any, any, infer Input>
? Input
: Schema
: never;
/**
* Merge states from middleware array
* Recursively combines state from all middleware
* @template T - Middleware array
*/
type InferMiddlewareStates<T extends readonly AgentMiddleware[]> =
T extends readonly [infer First, ...infer Rest]
? First extends AgentMiddleware
? InferMiddlewareState<First> &
(Rest extends readonly AgentMiddleware[]
? InferMiddlewareStates<Rest>
: {})
: {}
: {};
/**
* Extract middleware context
* @template T - Middleware type
*/
type InferMiddlewareContext<T> = T extends AgentMiddleware<
any,
infer ContextSchema,
any,
any
>
? InferSchemaInput<ContextSchema>
: never;
/**
* Merge contexts from middleware array
* Recursively combines context from all middleware
* @template T - Middleware array
*/
type InferMiddlewareContexts<T extends readonly AgentMiddleware[]> =
T extends readonly [infer First, ...infer Rest]
? First extends AgentMiddleware
? InferMiddlewareContext<First> &
(Rest extends readonly AgentMiddleware[]
? InferMiddlewareContexts<Rest>
: {})
: {}
: {};
/**
* Full merged state with built-in state
* Combines built-in agent state with middleware states
* @template T - Middleware array
*/
type InferMergedState<T> = BuiltInState & InferMiddlewareStates<T>;Middleware Type Inference Example:
import {
createMiddleware,
type InferMiddlewareState,
type InferMiddlewareStates,
} from "langchain";
import { z } from "zod";
// Create middleware with state
const analyticsMiddleware = createMiddleware({
name: "analytics",
stateSchema: z.object({
requestCount: z.number().default(0),
errorCount: z.number().default(0),
}),
});
// Infer single middleware state
type AnalyticsState = InferMiddlewareState<typeof analyticsMiddleware>;
// { requestCount: number; errorCount: number }
// Create another middleware
const sessionMiddleware = createMiddleware({
name: "session",
stateSchema: z.object({
sessionId: z.string(),
startTime: z.number(),
}),
});
// Combine middleware
const middleware = [analyticsMiddleware, sessionMiddleware] as const;
// Infer combined state
type CombinedState = InferMiddlewareStates<typeof middleware>;
// { requestCount: number; errorCount: number; sessionId: string; startTime: number }
// Use with agent
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
middleware: middleware,
});
// Agent state includes all middleware state
const result = await agent.invoke({
messages: [{ role: "user", content: "Hello" }],
requestCount: 0,
errorCount: 0,
sessionId: "session-123",
startTime: Date.now(),
});
// Type-safe access to all middleware state
console.log(result.requestCount);
console.log(result.sessionId);/**
* Check if all properties are optional
* Used to determine if context parameter should be required or optional in agent invocations
* @template T - Object type to check
* @example
* type AllOptional = IsAllOptional<{ a?: string; b?: number }>; // true
* type HasRequired = IsAllOptional<{ a: string; b?: number }>; // false
*/
type IsAllOptional<T> = {} extends Required<T> ? true : false;
/**
* Context optionality helper
* Determines if context should be required or optional based on schema properties
* @template TContext - Context type to wrap
* @example
* // All optional - context becomes optional
* type OptionalCtx = WithMaybeContext<{ a?: string }>;
* // { context?: { a?: string } }
*
* // Has required - context becomes required
* type RequiredCtx = WithMaybeContext<{ a: string }>;
* // { context: { a: string } }
*/
type WithMaybeContext<TContext> = IsAllOptional<TContext> extends true
? { context?: TContext }
: { context: TContext };
/**
* Any annotation root type
* Represents LangGraph annotation roots which can have arbitrary properties
*/
type AnyAnnotationRoot = {
[key: string]: any;
};
/**
* Normalized schema input
* Extracts input type from Zod schema or returns the schema type itself
* Handles both Zod schemas and plain object schemas
* @template TSchema - Schema to normalize
*/
type NormalizedSchemaInput<TSchema> = TSchema extends z.ZodType<
any,
any,
infer Input
>
? Input
: TSchema;
/**
* Extract structured response type from response format
* Handles single schemas, schema arrays (unions), and strategy types
* @template TResponseFormat - Response format configuration
*/
type InferStructuredResponse<TResponseFormat> =
TResponseFormat extends z.ZodType<infer Output, any, any>
? Output
: TResponseFormat extends readonly z.ZodType<any, any, any>[]
? ExtractZodArrayTypes<TResponseFormat>
: TResponseFormat extends ToolStrategy<infer T>
? T
: TResponseFormat extends ProviderStrategy<infer T>
? T
: TResponseFormat extends JsonSchemaFormat
? any
: TResponseFormat extends JsonSchemaFormat[]
? any
: undefined;
/**
* Agent name mode type
* Controls how agent name is included in messages
* - true: Include name in all messages
* - false: Never include name
* - "tool_messages": Include name only in tool messages
*/
type AgentNameMode = boolean | "tool_messages";
/**
* Extract output type from Zod schema
* @template T - Zod schema
*/
type InferZodOutput<T> = T extends z.ZodType<infer Output, any, any>
? Output
: never;
/**
* Extract input type from Zod schema
* @template T - Zod schema
*/
type InferZodInput<T> = T extends z.ZodType<any, any, infer Input>
? Input
: never;
/**
* Check if type is never
* @template T - Type to check
*/
type IsNever<T> = [T] extends [never] ? true : false;
/**
* Make properties optional if they have defaults
* @template T - Object type
*/
type WithDefaults<T> = {
[K in keyof T as T[K] extends { _def: { defaultValue: () => any } }
? K
: never]?: T[K];
} & {
[K in keyof T as T[K] extends { _def: { defaultValue: () => any } }
? never
: K]: T[K];
};
/**
* Prettify type for better IDE display
* Expands object types to show all properties
* @template T - Type to prettify
*/
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
/**
* Make all properties deeply optional
* @template T - Type to make optional
*/
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
/**
* Make all properties deeply required
* @template T - Type to make required
*/
type DeepRequired<T> = {
[K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K];
};
/**
* Union to intersection
* Converts union type to intersection type
* @template U - Union type
*/
type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never;
/**
* Merge two types with proper handling of optionality
* @template T - First type
* @template U - Second type
*/
type Merge<T, U> = Omit<T, keyof U> & U;
/**
* Extract required keys from object type
* @template T - Object type
*/
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
/**
* Extract optional keys from object type
* @template T - Object type
*/
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];import { createAgent, type InferAgentState, type InferAgentContext } from "langchain";
import { z } from "zod";
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
stateSchema: z.object({
count: z.number(),
}),
contextSchema: z.object({
userId: z.string(),
}),
});
// Infer state type
type AgentState = InferAgentState<typeof agent>;
// { messages: BaseMessage[]; count: number; structuredResponse?: any }
// Infer context type
type AgentContext = InferAgentContext<typeof agent>;
// { userId: string }import { createMiddleware, type InferMiddlewareState } from "langchain";
import { z } from "zod";
const middleware = createMiddleware({
name: "session",
stateSchema: z.object({
sessionId: z.string(),
startTime: z.number(),
}),
});
// Infer middleware state
type MiddlewareState = InferMiddlewareState<typeof middleware>;
// { sessionId: string; startTime: number }import { createAgent } from "langchain";
import { z } from "zod";
const StateSchema = z.object({
userPreferences: z.object({
theme: z.string(),
language: z.string(),
}),
});
const ContextSchema = z.object({
userId: z.string(),
requestId: z.string(),
});
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
stateSchema: StateSchema,
contextSchema: ContextSchema,
});
// Type-safe invocation
const result = await agent.invoke(
{
messages: [{ role: "user", content: "Hello" }],
userPreferences: {
theme: "dark",
language: "en",
},
},
{
context: {
userId: "user123",
requestId: "req456",
},
}
);
// Type-safe access
console.log(result.userPreferences.theme); // "dark"import { createAgent, type InferAgentResponse } from "langchain";
import { z } from "zod";
const ResponseSchema = z.object({
answer: z.string(),
confidence: z.number().min(0).max(1),
sources: z.array(z.string()),
});
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
responseFormat: ResponseSchema,
});
// Infer response type
type Response = InferAgentResponse<typeof agent>;
// { answer: string; confidence: number; sources: string[] }
const result = await agent.invoke({
messages: [{ role: "user", content: "What is TypeScript?" }],
});
// Type-safe access to structured response
const answer: string = result.structuredResponse.answer;
const confidence: number = result.structuredResponse.confidence;
const sources: string[] = result.structuredResponse.sources;import { createAgent, type InferAgentResponse } from "langchain";
import { z } from "zod";
const EmailResponse = z.object({
type: z.literal("email"),
to: z.string().email(),
subject: z.string(),
body: z.string(),
});
const TaskResponse = z.object({
type: z.literal("task"),
title: z.string(),
dueDate: z.string(),
priority: z.enum(["low", "medium", "high"]),
});
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
responseFormat: [EmailResponse, TaskResponse],
});
// Infer union type
type Response = InferAgentResponse<typeof agent>;
// { type: "email", to: string, subject: string, body: string } | { type: "task", title: string, dueDate: string, priority: "low" | "medium" | "high" }
const result = await agent.invoke({
messages: [{ role: "user", content: "Send email or create task" }],
});
// Type narrowing with discriminated union
if (result.structuredResponse.type === "email") {
console.log(result.structuredResponse.to); // TypeScript knows this exists
console.log(result.structuredResponse.subject);
} else {
console.log(result.structuredResponse.title); // TypeScript knows this exists
console.log(result.structuredResponse.priority);
}import { createMiddleware, createAgent, type InferMiddlewareStates } from "langchain";
import { z } from "zod";
const analyticsMiddleware = createMiddleware({
name: "analytics",
stateSchema: z.object({
requestCount: z.number().default(0),
}),
});
const sessionMiddleware = createMiddleware({
name: "session",
stateSchema: z.object({
sessionId: z.string(),
startTime: z.number(),
}),
});
const middleware = [analyticsMiddleware, sessionMiddleware] as const;
// Infer combined middleware state
type CombinedState = InferMiddlewareStates<typeof middleware>;
// { requestCount: number; sessionId: string; startTime: number }
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
middleware: middleware,
});
// Agent state includes all middleware state
const result = await agent.invoke({
messages: [{ role: "user", content: "Hello" }],
requestCount: 0,
sessionId: "session-123",
startTime: Date.now(),
});
// Type-safe access to middleware state
console.log(result.requestCount);
console.log(result.sessionId);
console.log(result.startTime);import { tool, type ToolsToMessageToolSet } from "langchain";
import { z } from "zod";
const searchTool = tool(
async ({ query }) => `Results for: ${query}`,
{
name: "search",
description: "Search for information",
schema: z.object({
query: z.string(),
}),
}
);
const calculatorTool = tool(
async ({ expression }) => String(eval(expression)),
{
name: "calculator",
description: "Evaluate expressions",
schema: z.object({
expression: z.string(),
}),
}
);
const tools = [searchTool, calculatorTool] as const;
// Create mapped type of tools by name
type ToolSet = ToolsToMessageToolSet<typeof tools>;
// { search: typeof searchTool; calculator: typeof calculatorTool }
// Access tools by name with type safety
const search: typeof searchTool = {} as ToolSet["search"];
const calculator: typeof calculatorTool = {} as ToolSet["calculator"];InferAgent* utilities instead of manually defining typesas const for middleware and tools arrays to preserve literal typestsc --extendedDiagnostics to identify slow type operationsUserProfileSchema, not Schema1)? modifiersimport { createAgent } from "langchain";
import { z } from "zod";
// Define state with optional admin properties
const StateSchema = z.object({
userId: z.string(),
isAdmin: z.boolean(),
adminPermissions: z.array(z.string()).optional(),
});
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
stateSchema: StateSchema,
});
// Type-safe conditional logic
const result = await agent.invoke({
messages: [{ role: "user", content: "Hello" }],
userId: "user123",
isAdmin: true,
adminPermissions: ["read", "write"],
});
if (result.isAdmin && result.adminPermissions) {
// TypeScript knows adminPermissions is defined here
console.log(result.adminPermissions.join(", "));
}import { createAgent, type InferAgentState } from "langchain";
import { z, type ZodType } from "zod";
function createTypedAgent<TStateSchema extends ZodType>(
stateSchema: TStateSchema,
model: string = "openai:gpt-4o"
) {
return createAgent({
model,
tools: [],
stateSchema,
});
}
// Create agents with different state schemas
const userAgent = createTypedAgent(
z.object({
userId: z.string(),
userName: z.string(),
})
);
const sessionAgent = createTypedAgent(
z.object({
sessionId: z.string(),
expiresAt: z.number(),
})
);
// Inferred types are different for each agent
type UserAgentState = InferAgentState<typeof userAgent>;
// { messages: BaseMessage[]; userId: string; userName: string; structuredResponse?: any }
type SessionAgentState = InferAgentState<typeof sessionAgent>;
// { messages: BaseMessage[]; sessionId: string; expiresAt: number; structuredResponse?: any }import { tool, createAgent, type InferMiddlewareTools } from "langchain";
import { z } from "zod";
// Create tool collections
const mathTools = [
tool(async ({ a, b }) => String(a + b), {
name: "add",
description: "Add numbers",
schema: z.object({ a: z.number(), b: z.number() }),
}),
tool(async ({ a, b }) => String(a * b), {
name: "multiply",
description: "Multiply numbers",
schema: z.object({ a: z.number(), b: z.number() }),
}),
] as const;
const searchTools = [
tool(async ({ query }) => `Results for: ${query}`, {
name: "search",
description: "Search",
schema: z.object({ query: z.string() }),
}),
] as const;
// Combine tool collections with type safety
const allTools = [...mathTools, ...searchTools] as const;
const agent = createAgent({
model: "openai:gpt-4o",
tools: allTools,
});
// TypeScript knows all available tool names
type ToolNames = typeof allTools[number]["name"];
// "add" | "multiply" | "search"If type inference isn't working as expected:
Use const assertions: Ensure you're using as const for arrays and objects
const middleware = [mw1, mw2] as const; // Good
const middleware = [mw1, mw2]; // Bad - loses literal typesDefine schemas before agent: Schemas must be defined before creating the agent
const StateSchema = z.object({ count: z.number() });
const agent = createAgent({ stateSchema: StateSchema }); // GoodCheck TypeScript version: Use TypeScript >=4.7 for best results
npm install -D typescript@latestUse explicit type annotations: As a fallback, manually annotate types
const result: State<MyStateType> = await agent.invoke(...);If you encounter circular type errors:
Use type aliases: Break circular dependencies with intermediate types
type AgentState = InferAgentState<typeof agent>;
type MiddlewareState = InferMiddlewareStates<typeof middleware>;Simplify schema structures: Avoid self-referential schemas
// Avoid
const Schema = z.object({
children: z.array(z.lazy(() => Schema)),
});
// Prefer
const Schema = z.object({
children: z.array(z.any()),
});Use any as escape hatch: When necessary, use any to break cycles
type MyType = InferAgentState<any>; // Last resortIf TypeScript compilation is slow:
type AgentState = InferAgentState<typeof agent>; // Compute oncetsc --extendedDiagnostics{
"compilerOptions": {
"skipLibCheck": true
}
}If type narrowing doesn't work with union types:
Use discriminated unions: Add a literal type field for discrimination
const Schema1 = z.object({ type: z.literal("A"), value: z.string() });
const Schema2 = z.object({ type: z.literal("B"), count: z.number() });Use type guards: Implement custom type guard functions
function isTypeA(obj: Response): obj is { type: "A", value: string } {
return obj.type === "A";
}Check discriminant property first: Always check the discriminant before accessing other properties
if (response.type === "A") {
console.log(response.value); // Now TypeScript knows the type
}// Agent type extraction
type InferAgentState<T> = /* Extract full merged state */;
type InferAgentResponse<T> = /* Extract structured response type */;
type InferAgentContext<T> = /* Extract full merged context */;
type InferAgentStateSchema<T> = /* Extract raw state schema */;
type InferAgentContextSchema<T> = /* Extract raw context schema */;
type InferAgentMiddleware<T> = /* Extract middleware array */;
type InferAgentTools<T> = /* Extract tools array */;
// Middleware type extraction
type InferMiddlewareSchema<T> = /* Extract middleware schema */;
type InferMiddlewareState<T> = /* Extract middleware state */;
type InferMiddlewareInputState<T> = /* Extract middleware input state */;
type InferMiddlewareStates<T> = /* Merge states from middleware array */;
type InferMiddlewareContext<T> = /* Extract middleware context */;
type InferMiddlewareContexts<T> = /* Merge contexts from middleware array */;
type InferMiddlewareTools<T> = /* Extract tools from middleware */;
type InferMiddlewareToolsArray<T> = /* Extract tools array from middleware array */;
// Schema type extraction
type InferSchemaInput<A> = /* Infer schema input type */;
type InferContextInput<ContextSchema> = /* Infer context input type */;
type InferZodOutput<T> = /* Extract output type from Zod schema */;
type InferZodInput<T> = /* Extract input type from Zod schema */;
type NormalizedSchemaInput<TSchema> = /* Normalize schema to input type */;
// Tool type utilities
type ExtractZodArrayTypes<T> = /* Extract union from Zod schema array */;
type ToolsToMessageToolSet<T> = /* Convert tools array to mapped type */;
type CombineTools<TAgentTools, TMiddleware> = /* Merge agent and middleware tools */;
// Helper types
type IsAllOptional<T> = /* Check if all properties optional */;
type WithMaybeContext<TContext> = /* Make context optional if all props optional */;
type IsNever<T> = /* Check if type is never */;
type Prettify<T> = /* Expand type for better IDE display */;
type DeepPartial<T> = /* Make all properties deeply optional */;
type DeepRequired<T> = /* Make all properties deeply required */;
type UnionToIntersection<U> = /* Convert union to intersection */;
type Merge<T, U> = /* Merge two types */;
type RequiredKeys<T> = /* Extract required keys */;
type OptionalKeys<T> = /* Extract optional keys */;
type WithDefaults<T> = /* Make properties with defaults optional */;
// Configuration types
type ResolveAgentTypeConfig<T> = /* Resolve agent type configuration */;
type DefaultAgentTypeConfig = /* Default agent type configuration */;
type AgentTypeConfig<TResponse, TState, TContext, TMiddleware, TTools> = /* Complete agent type config */;
type MiddlewareTypeConfig<TSchema, TContextSchema, TFullContext, TTools> = /* Complete middleware type config */;
// Response format types
type InferStructuredResponse<TResponseFormat> = /* Extract structured response type */;
type ResponseFormat = /* Union of all response format types */;
// Built-in types
type AgentNameMode = /* Agent name inclusion mode */;
type AnyAnnotationRoot = /* Any annotation root type */;The LangChain type system is built on several key principles:
For advanced users who need to understand how the type system works internally:
/**
* Internal: How agent type configuration is constructed
* This is built automatically from CreateAgentParams
*/
type AgentTypeConfig<TResponse, TState, TContext, TMiddleware, TTools> = {
response: TResponse; // From responseFormat
state: TState; // Merged from stateSchema + middleware state
context: TContext; // Merged from contextSchema + middleware context
middleware: TMiddleware; // Array of middleware instances
tools: TTools; // Merged from tools + middleware tools
};
/**
* Internal: How state is merged
* 1. Built-in state (messages, structuredResponse)
* 2. + Agent state schema
* 3. + Middleware state schemas (merged)
*/
type MergedState<
TStateSchema,
TMiddleware extends readonly AgentMiddleware[]
> = BuiltInState &
InferSchemaInput<TStateSchema> &
InferMiddlewareStates<TMiddleware>;
/**
* Internal: How context is merged
* 1. Agent context schema
* 2. + Middleware context schemas (merged)
*/
type MergedContext<
TContextSchema,
TMiddleware extends readonly AgentMiddleware[]
> = InferContextInput<TContextSchema> &
InferMiddlewareContexts<TMiddleware>;
/**
* Internal: How tools are merged
* 1. Agent tools
* 2. + Middleware tools (concatenated)
*/
type MergedTools<
TAgentTools extends readonly Tool[],
TMiddleware extends readonly AgentMiddleware[]
> = [...TAgentTools, ...InferMiddlewareTools<TMiddleware>];
/**
* Internal: Response format resolution
* Handles all response format variants:
* - Single Zod schema → infer output type
* - Array of Zod schemas → union of output types
* - Tool strategy → extract type parameter
* - Provider strategy → extract type parameter
* - JSON schema → any (no static type info)
*/
type ResolveResponseFormat<TResponseFormat> =
TResponseFormat extends z.ZodType<infer Output, any, any>
? Output
: TResponseFormat extends readonly z.ZodType<any, any, any>[]
? { [K in keyof TResponseFormat]: z.infer<TResponseFormat[K]> }[number]
: TResponseFormat extends ToolStrategy<infer T>
? T
: TResponseFormat extends ProviderStrategy<infer T>
? T
: undefined;import { createMiddleware, type AgentMiddleware } from "langchain";
import { z, type ZodType } from "zod";
/**
* Create a factory for middleware with a specific state pattern
*/
function createStatefulMiddleware<TSchema extends ZodType>(
name: string,
stateSchema: TSchema,
beforeModel?: (state: z.infer<TSchema>) => Promise<z.infer<TSchema>>
): AgentMiddleware<TSchema, never, never, []> {
return createMiddleware({
name,
stateSchema,
beforeModel: beforeModel as any,
});
}
// Usage
const counterMiddleware = createStatefulMiddleware(
"counter",
z.object({ count: z.number().default(0) }),
async (state) => {
return { ...state, count: state.count + 1 };
}
);import { tool, type Tool } from "langchain";
import { z, type ZodType } from "zod";
/**
* Builder for creating type-safe tool collections
*/
class ToolBuilder {
private tools: Tool[] = [];
add<TSchema extends ZodType>(
name: string,
description: string,
schema: TSchema,
func: (input: z.infer<TSchema>) => Promise<string>
) {
this.tools.push(tool(func as any, { name, description, schema }));
return this;
}
build() {
return this.tools as const;
}
}
// Usage
const tools = new ToolBuilder()
.add("search", "Search", z.object({ query: z.string() }), async ({ query }) => `Results for ${query}`)
.add("calculate", "Calculate", z.object({ expr: z.string() }), async ({ expr }) => String(eval(expr)))
.build();
// Tools are readonly and typed
type SearchTool = typeof tools[0];
type CalculateTool = typeof tools[1];import { createAgent, type InferAgentState } from "langchain";
import { z } from "zod";
/**
* Create agents with conditional state based on configuration
*/
function createConditionalAgent<TMode extends "basic" | "advanced">(
mode: TMode
) {
const basicSchema = z.object({
userId: z.string(),
});
const advancedSchema = z.object({
userId: z.string(),
adminLevel: z.number(),
permissions: z.array(z.string()),
});
return createAgent({
model: "openai:gpt-4o",
tools: [],
stateSchema: mode === "advanced" ? advancedSchema : basicSchema,
});
}
// Types are correctly inferred based on mode
const basicAgent = createConditionalAgent("basic");
type BasicState = InferAgentState<typeof basicAgent>;
// { messages: BaseMessage[]; userId: string; structuredResponse?: any }
const advancedAgent = createConditionalAgent("advanced");
type AdvancedState = InferAgentState<typeof advancedAgent>;
// { messages: BaseMessage[]; userId: string; adminLevel: number; permissions: string[]; structuredResponse?: any }