langchain
Version:
Typescript bindings for langchain
339 lines (337 loc) • 10.8 kB
text/typescript
import { AgentMiddleware } from "./types.cjs";
import { ContextSize, KeepSize, TokenCounter } from "./summarization.cjs";
import { BaseLanguageModel } from "@langchain/core/language_models/base";
import { BaseMessage } from "@langchain/core/messages";
//#region src/agents/middleware/contextEditing.d.ts
/**
* Protocol describing a context editing strategy.
*
* Implement this interface to create custom strategies for managing
* conversation context size. The `apply` method should modify the
* messages array in-place and return the updated token count.
*
* @example
* ```ts
* import { HumanMessage, type ContextEdit, type BaseMessage } from "langchain";
*
* class RemoveOldHumanMessages implements ContextEdit {
* constructor(private keepRecent: number = 10) {}
*
* async apply({ messages, countTokens }) {
* // Check current token count
* const tokens = await countTokens(messages);
*
* // Remove old human messages if over limit, keeping the most recent ones
* if (tokens > 50000) {
* const humanMessages: number[] = [];
*
* // Find all human message indices
* for (let i = 0; i < messages.length; i++) {
* if (HumanMessage.isInstance(messages[i])) {
* humanMessages.push(i);
* }
* }
*
* // Remove old human messages (keep only the most recent N)
* const toRemove = humanMessages.slice(0, -this.keepRecent);
* for (let i = toRemove.length - 1; i >= 0; i--) {
* messages.splice(toRemove[i]!, 1);
* }
* }
* }
* }
* ```
*/
interface ContextEdit {
/**
* Apply an edit to the message list, returning the new token count.
*
* This method should:
* 1. Check if editing is needed based on `tokens` parameter
* 2. Modify the `messages` array in-place (if needed)
* 3. Return the new token count after modifications
*
* @param params - Parameters for the editing operation
* @returns The updated token count after applying edits
*/
apply(params: {
/**
* Array of messages to potentially edit (modify in-place)
*/
messages: BaseMessage[];
/**
* Function to count tokens in a message array
*/
countTokens: TokenCounter;
/**
* Optional model instance for model profile information
*/
model?: BaseLanguageModel;
}): void | Promise<void>;
}
/**
* Configuration for clearing tool outputs when token limits are exceeded.
*/
interface ClearToolUsesEditConfig {
/**
* Trigger conditions for context editing.
* Can be a single condition object (all properties must be met) or an array of conditions (any condition must be met).
*
* @example
* ```ts
* // Single condition: trigger if tokens >= 100000 AND messages >= 50
* trigger: { tokens: 100000, messages: 50 }
*
* // Multiple conditions: trigger if (tokens >= 100000 AND messages >= 50) OR (tokens >= 50000 AND messages >= 100)
* trigger: [
* { tokens: 100000, messages: 50 },
* { tokens: 50000, messages: 100 }
* ]
*
* // Fractional trigger: trigger at 80% of model's max input tokens
* trigger: { fraction: 0.8 }
* ```
*/
trigger?: ContextSize | ContextSize[];
/**
* Context retention policy applied after editing.
* Specify how many tool results to preserve using messages, tokens, or fraction.
*
* @example
* ```ts
* // Keep 3 most recent tool results
* keep: { messages: 3 }
*
* // Keep tool results that fit within 1000 tokens
* keep: { tokens: 1000 }
*
* // Keep tool results that fit within 30% of model's max input tokens
* keep: { fraction: 0.3 }
* ```
*/
keep?: KeepSize;
/**
* Whether to clear the originating tool call parameters on the AI message.
* @default false
*/
clearToolInputs?: boolean;
/**
* List of tool names to exclude from clearing.
* @default []
*/
excludeTools?: string[];
/**
* Placeholder text inserted for cleared tool outputs.
* @default "[cleared]"
*/
placeholder?: string;
/**
* @deprecated Use `trigger: { tokens: value }` instead.
*/
triggerTokens?: number;
/**
* @deprecated Use `keep: { messages: value }` instead.
*/
keepMessages?: number;
/**
* @deprecated This property is deprecated and will be removed in a future version.
* Use `keep: { tokens: value }` or `keep: { messages: value }` instead to control retention.
*/
clearAtLeast?: number;
}
/**
* Strategy for clearing tool outputs when token limits are exceeded.
*
* This strategy mirrors Anthropic's `clear_tool_uses_20250919` behavior by
* replacing older tool results with a placeholder text when the conversation
* grows too large. It preserves the most recent tool results and can exclude
* specific tools from being cleared.
*
* @example
* ```ts
* import { ClearToolUsesEdit } from "langchain";
*
* const edit = new ClearToolUsesEdit({
* trigger: { tokens: 100000 }, // Start clearing at 100K tokens
* keep: { messages: 3 }, // Keep 3 most recent tool results
* excludeTools: ["important"], // Never clear "important" tool
* clearToolInputs: false, // Keep tool call arguments
* placeholder: "[cleared]", // Replacement text
* });
*
* // Multiple trigger conditions
* const edit2 = new ClearToolUsesEdit({
* trigger: [
* { tokens: 100000, messages: 50 },
* { tokens: 50000, messages: 100 }
* ],
* keep: { messages: 3 },
* });
*
* // Fractional trigger with model profile
* const edit3 = new ClearToolUsesEdit({
* trigger: { fraction: 0.8 }, // Trigger at 80% of model's max tokens
* keep: { fraction: 0.3 }, // Keep 30% of model's max tokens
* });
* ```
*/
declare class ClearToolUsesEdit implements ContextEdit {
#private;
trigger: ContextSize | ContextSize[];
keep: KeepSize;
clearToolInputs: boolean;
excludeTools: Set<string>;
placeholder: string;
model: BaseLanguageModel;
clearAtLeast: number;
constructor(config?: ClearToolUsesEditConfig);
apply(params: {
messages: BaseMessage[];
model: BaseLanguageModel;
countTokens: TokenCounter;
}): Promise<void>;
}
/**
* Configuration for the Context Editing Middleware.
*/
interface ContextEditingMiddlewareConfig {
/**
* Sequence of edit strategies to apply. Defaults to a single
* ClearToolUsesEdit mirroring Anthropic defaults.
*/
edits?: ContextEdit[];
/**
* Whether to use approximate token counting (faster, less accurate)
* or exact counting implemented by the chat model (potentially slower, more accurate).
* Currently only OpenAI models support exact counting.
* @default "approx"
*/
tokenCountMethod?: "approx" | "model";
}
/**
* Middleware that automatically prunes tool results to manage context size.
*
* This middleware applies a sequence of edits when the total input token count
* exceeds configured thresholds. By default, it uses the `ClearToolUsesEdit` strategy
* which mirrors Anthropic's `clear_tool_uses_20250919` behaviour by clearing older
* tool results once the conversation exceeds 100,000 tokens.
*
* ## Basic Usage
*
* Use the middleware with default settings to automatically manage context:
*
* @example Basic usage with defaults
* ```ts
* import { contextEditingMiddleware } from "langchain";
* import { createAgent } from "langchain";
*
* const agent = createAgent({
* model: "anthropic:claude-sonnet-4-5",
* tools: [searchTool, calculatorTool],
* middleware: [
* contextEditingMiddleware(),
* ],
* });
* ```
*
* The default configuration:
* - Triggers when context exceeds **100,000 tokens**
* - Keeps the **3 most recent** tool results
* - Uses **approximate token counting** (fast)
* - Does not clear tool call arguments
*
* ## Custom Configuration
*
* Customize the clearing behavior with `ClearToolUsesEdit`:
*
* @example Custom ClearToolUsesEdit configuration
* ```ts
* import { contextEditingMiddleware, ClearToolUsesEdit } from "langchain";
*
* // Single condition: trigger if tokens >= 50000 AND messages >= 20
* const agent1 = createAgent({
* model: "anthropic:claude-sonnet-4-5",
* tools: [searchTool, calculatorTool],
* middleware: [
* contextEditingMiddleware({
* edits: [
* new ClearToolUsesEdit({
* trigger: { tokens: 50000, messages: 20 },
* keep: { messages: 5 },
* excludeTools: ["search"],
* clearToolInputs: true,
* }),
* ],
* tokenCountMethod: "approx",
* }),
* ],
* });
*
* // Multiple conditions: trigger if (tokens >= 50000 AND messages >= 20) OR (tokens >= 30000 AND messages >= 50)
* const agent2 = createAgent({
* model: "anthropic:claude-sonnet-4-5",
* tools: [searchTool, calculatorTool],
* middleware: [
* contextEditingMiddleware({
* edits: [
* new ClearToolUsesEdit({
* trigger: [
* { tokens: 50000, messages: 20 },
* { tokens: 30000, messages: 50 },
* ],
* keep: { messages: 5 },
* }),
* ],
* }),
* ],
* });
*
* // Fractional trigger with model profile
* const agent3 = createAgent({
* model: chatModel,
* tools: [searchTool, calculatorTool],
* middleware: [
* contextEditingMiddleware({
* edits: [
* new ClearToolUsesEdit({
* trigger: { fraction: 0.8 }, // Trigger at 80% of model's max tokens
* keep: { fraction: 0.3 }, // Keep 30% of model's max tokens
* model: chatModel,
* }),
* ],
* }),
* ],
* });
* ```
*
* ## Custom Editing Strategies
*
* Implement your own context editing strategy by creating a class that
* implements the `ContextEdit` interface:
*
* @example Custom editing strategy
* ```ts
* import { contextEditingMiddleware, type ContextEdit, type TokenCounter } from "langchain";
* import type { BaseMessage } from "@langchain/core/messages";
*
* class CustomEdit implements ContextEdit {
* async apply(params: {
* tokens: number;
* messages: BaseMessage[];
* countTokens: TokenCounter;
* }): Promise<number> {
* // Implement your custom editing logic here
* // and apply it to the messages array, then
* // return the new token count after edits
* return countTokens(messages);
* }
* }
* ```
*
* @param config - Configuration options for the middleware
* @returns A middleware instance that can be used with `createAgent`
*/
declare function contextEditingMiddleware(config?: ContextEditingMiddlewareConfig): AgentMiddleware<undefined, undefined, any>;
//#endregion
export { ClearToolUsesEdit, ClearToolUsesEditConfig, ContextEdit, ContextEditingMiddlewareConfig, contextEditingMiddleware };
//# sourceMappingURL=contextEditing.d.cts.map