From 4aeb0dd303f45b5608882e3aaf867e2256600594 Mon Sep 17 00:00:00 2001 From: Ignazio De Santis <132545917+IgnazioDS@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:16:39 +0800 Subject: [PATCH] fix(langgraph): forward *args/**kwargs through node wrappers to preserve config kwarg LangGraph node functions may accept a second argument: config: RunnableConfig. The node wrappers in _wrap_add_node used fixed (state,) signatures, which caused LangGraph to call the wrapped function with config= and receive: TypeError: agent_node() got an unexpected keyword argument 'config' Fix: change both wrapped_node_async and wrapped_node_sync to use *args, **kwargs so any additional arguments passed by the LangGraph runtime are transparently forwarded to the original node function. Fixes #1295 --- .../agentic/langgraph/instrumentation.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/agentops/instrumentation/agentic/langgraph/instrumentation.py b/agentops/instrumentation/agentic/langgraph/instrumentation.py index 8f778eee4..375d94a48 100644 --- a/agentops/instrumentation/agentic/langgraph/instrumentation.py +++ b/agentops/instrumentation/agentic/langgraph/instrumentation.py @@ -364,8 +364,11 @@ def create_wrapped_node(original_func): if inspect.iscoroutinefunction(original_func): @wraps(original_func) - async def wrapped_node_async(state): + async def wrapped_node_async(*args, **kwargs): # Track node execution in parent graph span + # LangGraph passes state as the first positional arg; additional + # kwargs (e.g. config: RunnableConfig) are forwarded transparently. + state = args[0] if args else kwargs.get("state") self._track_node_execution(key) # Check if this node contains an LLM call @@ -385,8 +388,8 @@ async def wrapped_node_async(state): ) try: - # Call the original function - result = await original_func(state) + # Forward all args so that optional kwargs like config are preserved + result = await original_func(*args, **kwargs) # Extract LLM information from the result self._extract_llm_info_from_result(span, state, result) @@ -399,12 +402,15 @@ async def wrapped_node_async(state): raise else: # Non-LLM node, just execute normally - return await original_func(state) + return await original_func(*args, **kwargs) else: @wraps(original_func) - def wrapped_node_sync(state): + def wrapped_node_sync(*args, **kwargs): # Track node execution in parent graph span + # LangGraph passes state as the first positional arg; additional + # kwargs (e.g. config: RunnableConfig) are forwarded transparently. + state = args[0] if args else kwargs.get("state") self._track_node_execution(key) # Check if this node contains an LLM call @@ -423,8 +429,8 @@ def wrapped_node_sync(state): ) try: - # Call the original function - result = original_func(state) + # Forward all args so that optional kwargs like config are preserved + result = original_func(*args, **kwargs) # Extract LLM information from the result self._extract_llm_info_from_result(span, state, result) @@ -437,7 +443,7 @@ def wrapped_node_sync(state): raise else: # Non-LLM node, just execute normally - return original_func(state) + return original_func(*args, **kwargs) return wrapped_node_sync