import { ToolNode } from '@langchain/langgraph/prebuilt'; import { Runnable, RunnableConfig } from '@langchain/core/runnables'; import type { UsageMetadata, BaseMessage } from '@langchain/core/messages'; import type { ToolCall } from '@langchain/core/messages/tool'; import type * as t from '@/types'; import { Providers } from '@/common'; import { type ContextAnalytics } from '@/utils/contextAnalytics'; import { ToolNode as CustomToolNode } from '@/tools/ToolNode'; import { AgentContext } from '@/agents/AgentContext'; import { HandlerRegistry } from '@/events'; import { StreamingToolCallBuffer } from '@/tools/StreamingToolCallBuffer'; export declare abstract class Graph { abstract resetValues(): void; abstract initializeTools({ currentTools, currentToolMap, }: { currentTools?: t.GraphTools; currentToolMap?: t.ToolMap; }): CustomToolNode | ToolNode; abstract initializeModel({ currentModel, tools, clientOptions, }: { currentModel?: t.ChatModel; tools?: t.GraphTools; clientOptions?: t.ClientOptions; }): Runnable; abstract getRunMessages(): BaseMessage[] | undefined; abstract getContentParts(): t.MessageContentComplex[] | undefined; abstract generateStepId(stepKey: string): [string, number]; abstract getKeyList(metadata: Record | undefined): (string | number | undefined)[]; abstract getStepKey(metadata: Record | undefined): string; abstract checkKeyList(keyList: (string | number | undefined)[]): boolean; abstract getStepIdByKey(stepKey: string, index?: number): string; abstract getRunStep(stepId: string): t.RunStep | undefined; abstract dispatchRunStep(stepKey: string, stepDetails: t.StepDetails, metadata?: Record): Promise; abstract dispatchRunStepDelta(id: string, delta: t.ToolCallDelta): Promise; abstract dispatchMessageDelta(id: string, delta: t.MessageDelta): Promise; abstract dispatchReasoningDelta(stepId: string, delta: t.ReasoningDelta): Promise; abstract handleToolCallCompleted(data: t.ToolEndData, metadata?: Record, omitOutput?: boolean): Promise; abstract createCallModel(agentId?: string, currentModel?: t.ChatModel): (state: T, config?: RunnableConfig) => Promise>; messageStepHasToolCalls: Map; messageIdsByStepKey: Map; prelimMessageIdsByStepKey: Map; config: RunnableConfig | undefined; contentData: t.RunStep[]; stepKeyIds: Map; contentIndexMap: Map; toolCallStepIds: Map; signal?: AbortSignal; /** Set of invoked tool call IDs from non-message run steps completed mid-run, if any */ invokedToolIds?: Set; handlerRegistry: HandlerRegistry | undefined; /** Optional hook registry threaded down from Run; consumed by ToolNode for tool-lifecycle hooks. */ hookRegistry?: import('@/hooks').HookRegistry; /** * Optional tool-output reference registry threaded down from Run * (upstream PR #114). When set, every ToolNode built by this graph * stores successful outputs here and resolves `{{toolturn}}` * placeholders in args before invoking the tool. * * @deprecated Prefer `toolOutputReferences` config + the lazy * `getOrCreateToolOutputRegistry()` accessor — keeps the registry * lifecycle explicit (cleared in `clearHeavyState()`). Existing * direct-set callers still work for backwards compat. */ toolOutputRegistry?: import('@/tools/toolOutputReferences').ToolOutputReferenceRegistry; /** * Run-scoped tool output reference configuration (upstream PR #117). * When `enabled` is true, the graph lazily allocates a single * `ToolOutputReferenceRegistry` on first `getOrCreateToolOutputRegistry()` * call and shares it with every ToolNode the graph compiles, so * cross-agent `{{toolturn}}` substitutions resolve. */ toolOutputReferences?: import('@/types/tools').ToolOutputReferencesConfig; /** * Lazy single-instance registry for the run. Constructed on first * `getOrCreateToolOutputRegistry()` call when * `toolOutputReferences.enabled` is true. Cleared (and recreated on * next access) by `clearHeavyState()`. */ private _toolOutputRegistry?; /** * Returns the shared `ToolOutputReferenceRegistry` for this run, * constructing it on first access. Returns `undefined` when the * feature is disabled. All ToolNodes compiled from this graph share * this single instance so cross-agent `{{...}}` references resolve. * * @internal Public so `attemptInvoke` can read it through the typed * `InvokeContext` and project ToolMessages into LLM-facing annotated * copies right before each provider call (see `annotateMessagesForLLM`). * Host code should not call this directly — registry mutations outside * the ToolNode lifecycle break the partitioning, eviction, and * turn-counter invariants. */ getOrCreateToolOutputRegistry(): import('@/tools/toolOutputReferences').ToolOutputReferenceRegistry | undefined; /** * Tool session contexts for automatic state persistence across tool invocations. * Keyed by tool name (e.g., Constants.EXECUTE_CODE). * Currently supports code execution session tracking (session_id, files). */ sessions: t.ToolSessionMap; /** * Streaming tool call buffer — accumulates raw arg strings during streaming * so that truncated tool call content can be recovered by the ToolNode. * Fed by handleToolCallChunks, consumed by ToolNode.run when args are incomplete. */ streamingToolCallBuffer: StreamingToolCallBuffer; /** * Clears heavy references to allow GC to reclaim memory held by * LangGraph's internal config / AsyncLocalStorage RunTree chain. * Call after a run completes and content has been extracted. */ clearHeavyState(): void; } export declare class StandardGraph extends Graph { overrideModel?: t.ChatModel; /** Optional compile options passed into workflow.compile() */ compileOptions?: t.CompileOptions | undefined; messages: BaseMessage[]; runId: string | undefined; startIndex: number; signal?: AbortSignal; /** Cached summary from the first prune in this run. * Reused for subsequent prunes to avoid blocking LLM calls on every tool iteration. */ private _cachedRunSummary; /** EMA-based pruning calibration state — smooths token budget adjustments across iterations */ private _pruneCalibration; /** Run-scoped tool discovery cache — avoids re-parsing conversation history on every iteration */ private _toolDiscoveryCache; /** * SCALE: Tracks whether a summary call is already in-flight for this Graph instance. * Prevents multiple concurrent summary LLM calls when rapid tool iterations each * trigger pruning. At 2000 users with 3+ tool calls per turn, this prevents * 6000+ summary calls/turn from becoming 2000. */ private _summaryInFlight; /** Messages accumulated across tool iterations while a summary call is in-flight */ private _pendingMessagesToRefine; /** Map of agent contexts by agent ID */ agentContexts: Map; /** Default agent ID to use */ defaultAgentId: string; /** Normalized finish/stop reason from the last LLM invocation */ lastFinishReason: string | undefined; constructor({ runId, signal, agents, tokenCounter, indexTokenCountMap, }: t.StandardGraphInput); resetValues(keepContent?: boolean): void; clearHeavyState(): void; /** * Returns clientOptions with a reduced thinking budget for subsequent * ReAct loop iterations (tool-result turns). * * **Rationale:** The first LLM call in a conversation processes the user's * original query and may benefit from deep extended thinking. Subsequent * iterations — where the model receives tool results and decides whether * to call another tool or produce a final response — require minimal * reasoning. Reducing the thinking budget from the user's configured * value to TOOL_TURN_THINKING_BUDGET (1024 tokens) cuts wall-clock * latency by ~15-20s per iteration, compounding across multi-tool flows. * * Provider handling: * - **Anthropic (direct):** Reduces `thinking.budget_tokens` if > threshold * - **Bedrock (Anthropic models):** Reduces `additionalModelRequestFields.thinking.budget_tokens` * - **VertexAI / Google:** Reduces `thinkingConfig.thinkingBudget` if > threshold * - **All others:** Returns clientOptions unchanged (no-op) * * @param clientOptions - The original client options from AgentContext * @param provider - The LLM provider enum value * @returns Shallow-cloned clientOptions with reduced thinking budget, or the original if no reduction needed */ getAdaptiveClientOptions(clientOptions: t.ClientOptions, provider: Providers): t.ClientOptions; /** * Determines whether summarization should trigger based on SummarizationConfig. * * Supports three trigger strategies: * - contextPercentage (default): Trigger when context utilization >= threshold% * - messageCount: Trigger when pruned message count >= threshold * - tokenThreshold: Trigger when total estimated tokens >= threshold * * When no config is provided, always triggers (preserves backward compatibility). * * @param prunedMessageCount - Number of messages that were pruned * @param maxContextTokens - Maximum context token budget * @param indexTokenCountMap - Token count map by message index * @param instructionTokens - Token count for instructions/system message * @param config - Optional SummarizationConfig * @returns Whether summarization should be triggered */ private shouldTriggerSummarization; /** * Returns the normalized finish/stop reason from the last LLM invocation. * Used by callers to detect when the response was truncated due to max_tokens. */ getLastFinishReason(): string | undefined; /** * Estimates a human-friendly description of the conversation timeframe based on message count. * Uses rough heuristics to provide context about how much history is available. * * @param messageCount - Number of messages in the remaining context * @returns A friendly description like "the last few minutes", "the past hour", etc. */ getContextTimeframeDescription(messageCount: number): string; getRunStep(stepId: string): t.RunStep | undefined; getAgentContext(metadata: Record | undefined): AgentContext; getStepKey(metadata: Record | undefined): string; getStepIdByKey(stepKey: string, index?: number): string; generateStepId(stepKey: string): [string, number]; getKeyList(metadata: Record | undefined): (string | number | undefined)[]; checkKeyList(keyList: (string | number | undefined)[]): boolean; getRunMessages(): BaseMessage[] | undefined; getContentParts(): t.MessageContentComplex[] | undefined; /** * Get all run steps, optionally filtered by agent ID */ getRunSteps(agentId?: string): t.RunStep[]; /** * Get run steps grouped by agent ID */ getRunStepsByAgent(): Map; /** * Get agent IDs that participated in this run */ getActiveAgentIds(): string[]; /** * Maps contentPart indices to agent IDs for post-run analysis * Returns a map where key is the contentPart index and value is the agentId */ getContentPartAgentMap(): Map; /** * Get the context breakdown from the primary agent for admin token tracking. * Returns detailed token counts for instructions, tools, etc. */ getContextBreakdown(): { instructions: number; artifacts: number; tools: number; toolCount: number; toolContext: number; total: number; toolsDetail: Array<{ name: string; tokens: number; }>; toolContextDetail: Array<{ name: string; tokens: number; }>; } | null; /** * Get the latest context analytics from the graph. * Returns metrics like utilization %, TOON stats, message breakdown. */ getContextAnalytics(): ContextAnalytics | null; /** Store the latest context analytics for retrieval after run */ private lastContextAnalytics; createSystemRunnable({ provider, clientOptions, instructions, additional_instructions, }: { provider?: Providers; clientOptions?: t.ClientOptions; instructions?: string; additional_instructions?: string; }): t.SystemRunnable | undefined; initializeTools({ currentTools, currentToolMap, agentContext, }: { currentTools?: t.GraphTools; currentToolMap?: t.ToolMap; agentContext?: AgentContext; }): CustomToolNode | ToolNode; initializeModel({ provider, tools, clientOptions, }: { provider: Providers; tools?: t.GraphTools; clientOptions?: t.ClientOptions; }): Runnable; overrideTestModel(responses: string[], sleep?: number, toolCalls?: ToolCall[]): void; getNewModel({ provider, clientOptions, }: { provider: Providers; clientOptions?: t.ClientOptions; }): t.ChatModelInstance; getUsageMetadata(finalMessage?: BaseMessage): Partial | undefined; /** Execute model invocation with streaming support */ private attemptInvoke; /** * Execute model invocation with structured output. * Uses native constrained decoding (jsonSchema method) for supported providers, * or falls back to withStructuredOutput with functionCalling/jsonMode. * * Native mode uses provider APIs directly: * - Anthropic: output_config.format via LangChain's method: 'json_schema' * - OpenAI/Azure: response_format.json_schema via LangChain's method: 'jsonSchema' * - Bedrock: falls back to functionCalling (LangChain doesn't support native yet) */ private attemptStructuredInvoke; cleanupSignalListener(currentModel?: t.ChatModel): void; /** * Perform structured output invocation: creates a fresh model without tools bound, * removes thinking configuration, invokes with the schema, emits the event, * and returns a clean AIMessageChunk without tool_calls. * * Used by both the immediate path (no tools) and the deferred path (after tool use). */ private performStructuredOutput; createCallModel(agentId?: string): (state: t.BaseGraphState, config?: RunnableConfig) => Promise>; createAgentNode(agentId: string): t.CompiledAgentWorfklow; createWorkflow(): t.CompiledStateWorkflow; /** * Indicates if this is a multi-agent graph. * Override in MultiAgentGraph to return true. * Used to conditionally include agentId in RunStep for frontend rendering. */ protected isMultiAgentGraph(): boolean; /** * Get the parallel group ID for an agent, if any. * Override in MultiAgentGraph to provide actual group IDs. * Group IDs are incrementing numbers (1, 2, 3...) reflecting execution order. * @param _agentId - The agent ID to look up * @returns undefined for StandardGraph (no parallel groups), or group number for MultiAgentGraph */ protected getParallelGroupIdForAgent(_agentId: string): number | undefined; /** * Dispatches a run step to the client, returns the step ID */ dispatchRunStep(stepKey: string, stepDetails: t.StepDetails, metadata?: Record): Promise; handleToolCallCompleted(data: t.ToolEndData, metadata?: Record, omitOutput?: boolean): Promise; /** * Static version of handleToolCallError to avoid creating strong references * that prevent garbage collection */ static handleToolCallErrorStatic(graph: StandardGraph, data: t.ToolErrorData, metadata?: Record): Promise; /** * Instance method that delegates to the static method * Kept for backward compatibility */ handleToolCallError(data: t.ToolErrorData, metadata?: Record): Promise; dispatchRunStepDelta(id: string, delta: t.ToolCallDelta): Promise; dispatchMessageDelta(id: string, delta: t.MessageDelta): Promise; dispatchReasoningDelta: (stepId: string, delta: t.ReasoningDelta) => Promise; }