TypeScript framework for building LLM-powered applications with agents, tools, middleware, and model interoperability
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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 }docs