Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional kwargs key prompt_tokens already exists in left dict and value has unsupported type <class 'int'> in langchain-core/utils/_merge.py merge_dict() function when running with anthropic.claude-3-sonnet #29116

Open
5 tasks done
AkashBais opened this issue Jan 9, 2025 · 0 comments
Labels
🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature

Comments

@AkashBais
Copy link

AkashBais commented Jan 9, 2025

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangChain documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).

Example Code

Below is the method from _merge.py that raises the error.
In my code the prebuilt ReAct agent sits inside the LangGraph node
[Won't be possible to have the entire code in here but the same type of agents executes the first time and fails the second ]


def merge_dicts(left: dict[str, Any], *others: dict[str, Any]) -> dict[str, Any]:
    """Merge many dicts, handling specific scenarios where a key exists in both
    dictionaries but has a value of None in 'left'. In such cases, the method uses the
    value from 'right' for that key in the merged dictionary.
    Args:
        left: The first dictionary to merge.
        others: The other dictionaries to merge.

    Returns:
        The merged dictionary.

    Raises:
        TypeError: If the key exists in both dictionaries but has a different type.
        TypeError: If the value has an unsupported type.

    Example:
        If left = {"function_call": {"arguments": None}} and
        right = {"function_call": {"arguments": "{\n"}}
        then, after merging, for the key "function_call",
        the value from 'right' is used,
        resulting in merged = {"function_call": {"arguments": "{\n"}}.
    """
    merged = left.copy()
    for right in others:
        for right_k, right_v in right.items():
            if right_k not in merged or right_v is not None and merged[right_k] is None:
                merged[right_k] = right_v
            elif right_v is None:
                continue
            elif type(merged[right_k]) is not type(right_v):
                msg = (
                    f'additional_kwargs["{right_k}"] already exists in this message,'
                    " but with a different type."
                )
                raise TypeError(msg)
            elif isinstance(merged[right_k], str):
                # TODO: Add below special handling for 'type' key in 0.3 and remove
                # merge_lists 'type' logic.
                #
                # if right_k == "type":
                #     if merged[right_k] == right_v:
                #         continue
                #     else:
                #         raise ValueError(
                #             "Unable to merge. Two different values seen for special "
                #             f"key 'type': {merged[right_k]} and {right_v}. 'type' "
                #             "should either occur once or have the same value across "
                #             "all dicts."
                #         )
                merged[right_k] += right_v
            elif isinstance(merged[right_k], dict):
                merged[right_k] = merge_dicts(merged[right_k], right_v)
            elif isinstance(merged[right_k], list):
                merged[right_k] = merge_lists(merged[right_k], right_v)
            elif merged[right_k] == right_v:
                continue
            else:
                msg = (
                    f"Additional kwargs key {right_k} already exists in left dict and "
                    f"value has unsupported type {type(merged[right_k])}."
                )
                raise TypeError(msg)
    return merged

Error Message and Stack Trace (if applicable)


TypeError Traceback (most recent call last)
Cell In[12], line 7
2 userInput = '''
3 Develop Business Challenges and Opportunities (BCOs) for brand based on its Strategic Imperatives (SIs).
4 '''
5 message = HumanMessage(content = userInput )
----> 7 graph.invoke(
8 input = {"messages" : [message],
9 "brand" : 'brand',
10 "primary_competitors" : ["competitor 1", "competitor 2", "competitor 3"],
11 "brand_research" : [],
12 "strategic_imperatives" : ["SI-1",
13 "SI-2",
14 "SI-3",
15 "SI-4",
16 "SI-5",
17 "SI-6'),
18 "stratagic_imperatives_research" : [],
19 "plan" : [],
20 "next_actor" : '',
21 "next_task" : '',
22 "sender" : '',
23 },
24 config = {"configurable": {"thread_id": "42"}, "recursion_limit": 30} ,
25
26
27 )
28 # ToolMessage

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/init.py:1940, in Pregel.invoke(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs)
1938 else:
1939 chunks = []
-> 1940 for chunk in self.stream(
1941 input,
1942 config,
1943 stream_mode=stream_mode,
1944 output_keys=output_keys,
1945 interrupt_before=interrupt_before,
1946 interrupt_after=interrupt_after,
1947 debug=debug,
1948 **kwargs,
1949 ):
1950 if stream_mode == "values":
1951 latest = chunk

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/init.py:1660, in Pregel.stream(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)
1654 # Similarly to Bulk Synchronous Parallel / Pregel model
1655 # computation proceeds in steps, while there are channel updates
1656 # channel updates from step N are only visible in step N+1
1657 # channels are guaranteed to be immutable for the duration of the step,
1658 # with channel updates applied only at the transition between steps
1659 while loop.tick(input_keys=self.input_channels):
-> 1660 for _ in runner.tick(
1661 loop.tasks.values(),
1662 timeout=self.step_timeout,
1663 retry_policy=self.retry_policy,
1664 get_waiter=get_waiter,
1665 ):
1666 # emit output
1667 yield from output()
1668 # emit output

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/runner.py:167, in PregelRunner.tick(self, tasks, reraise, timeout, retry_policy, get_waiter)
165 t = tasks[0]
166 try:
--> 167 run_with_retry(
168 t,
169 retry_policy,
170 configurable={
171 CONFIG_KEY_SEND: partial(writer, t),
172 CONFIG_KEY_CALL: partial(call, t),
173 },
174 )
175 self.commit(t, None)
176 except Exception as exc:

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/retry.py:40, in run_with_retry(task, retry_policy, configurable)
38 task.writes.clear()
39 # run the task
---> 40 return task.proc.invoke(task.input, config)
41 except ParentCommand as exc:
42 ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]

File /opt/conda/lib/python3.11/site-packages/langgraph/utils/runnable.py:408, in RunnableSeq.invoke(self, input, config, **kwargs)
404 config = patch_config(
405 config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
406 )
407 if i == 0:
--> 408 input = step.invoke(input, config, **kwargs)
409 else:
410 input = step.invoke(input, config)

File /opt/conda/lib/python3.11/site-packages/langgraph/utils/runnable.py:184, in RunnableCallable.invoke(self, input, config, **kwargs)
182 else:
183 context.run(_set_config_context, config)
--> 184 ret = context.run(self.func, input, **kwargs)
185 if isinstance(ret, Runnable) and self.recurse:
186 return ret.invoke(input, config)

File ~/MUltiAgent_SI_to_BCO/graph/workflow.py:74, in workflow.init_graph..(state)
68 self.workflow = StateGraph(state)
70 self.workflow.add_node("brand_research_agent",lambda state: agent_node(state = state,
71 agent = self.agents["brand_research_agent"],
72 name = "brand_research_agent",))
---> 74 self.workflow.add_node("si_research_agent",lambda state: agent_node(state = state,
75 agent = self.agents["si_research_agent"],
76 name = "si_research_agent",))
78 self.workflow.add_node("bco_planning_agent",lambda state: agent_node(state = state,
79 agent = self.agents["bco_planning_agent"],
80 name = "bco_planning_agent",))
82 self.workflow.add_node("bco_formulation_agent",lambda state: agent_node(state = state,
83 agent = self.agents["bco_formulation_agent"],
84 name = "bco_formulation_agent",))

File ~/MUltiAgent_SI_to_BCO/graph/nodes.py:78, in agent_node(state, agent, name)
75 except Exception as e:
76 # Log and raise any exceptions that occur
77 logger.error(f"Error in executing {name} node: {str(e)}")
---> 78 raise e

File ~/MUltiAgent_SI_to_BCO/graph/nodes.py:39, in agent_node(state, agent, name)
36 logger.info(f"executing agent {name}")
38 # Invoke the agent with the current state
---> 39 response = agent.invoke(state)
41 # Extract the content from the response
42 content = response['output'] if isinstance(response, dict) and 'output' in response else response

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/init.py:1940, in Pregel.invoke(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs)
1938 else:
1939 chunks = []
-> 1940 for chunk in self.stream(
1941 input,
1942 config,
1943 stream_mode=stream_mode,
1944 output_keys=output_keys,
1945 interrupt_before=interrupt_before,
1946 interrupt_after=interrupt_after,
1947 debug=debug,
1948 **kwargs,
1949 ):
1950 if stream_mode == "values":
1951 latest = chunk

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/init.py:1660, in Pregel.stream(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)
1654 # Similarly to Bulk Synchronous Parallel / Pregel model
1655 # computation proceeds in steps, while there are channel updates
1656 # channel updates from step N are only visible in step N+1
1657 # channels are guaranteed to be immutable for the duration of the step,
1658 # with channel updates applied only at the transition between steps
1659 while loop.tick(input_keys=self.input_channels):
-> 1660 for _ in runner.tick(
1661 loop.tasks.values(),
1662 timeout=self.step_timeout,
1663 retry_policy=self.retry_policy,
1664 get_waiter=get_waiter,
1665 ):
1666 # emit output
1667 yield from output()
1668 # emit output

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/runner.py:167, in PregelRunner.tick(self, tasks, reraise, timeout, retry_policy, get_waiter)
165 t = tasks[0]
166 try:
--> 167 run_with_retry(
168 t,
169 retry_policy,
170 configurable={
171 CONFIG_KEY_SEND: partial(writer, t),
172 CONFIG_KEY_CALL: partial(call, t),
173 },
174 )
175 self.commit(t, None)
176 except Exception as exc:

File /opt/conda/lib/python3.11/site-packages/langgraph/pregel/retry.py:40, in run_with_retry(task, retry_policy, configurable)
38 task.writes.clear()
39 # run the task
---> 40 return task.proc.invoke(task.input, config)
41 except ParentCommand as exc:
42 ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]

File /opt/conda/lib/python3.11/site-packages/langgraph/utils/runnable.py:408, in RunnableSeq.invoke(self, input, config, **kwargs)
404 config = patch_config(
405 config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
406 )
407 if i == 0:
--> 408 input = step.invoke(input, config, **kwargs)
409 else:
410 input = step.invoke(input, config)

File /opt/conda/lib/python3.11/site-packages/langgraph/utils/runnable.py:176, in RunnableCallable.invoke(self, input, config, **kwargs)
174 context = copy_context()
175 context.run(_set_config_context, child_config)
--> 176 ret = context.run(self.func, input, **kwargs)
177 except BaseException as e:
178 run_manager.on_chain_error(e)

File /opt/conda/lib/python3.11/site-packages/langgraph/prebuilt/chat_agent_executor.py:560, in create_react_agent..call_model(state, config)
558 def call_model(state: AgentState, config: RunnableConfig) -> AgentState:
559 _validate_chat_history(state["messages"])
--> 560 response = model_runnable.invoke(state, config)
561 has_tool_calls = isinstance(response, AIMessage) and response.tool_calls
562 all_tools_return_direct = (
563 all(call["name"] in should_return_direct for call in response.tool_calls)
564 if isinstance(response, AIMessage)
565 else False
566 )

File /opt/conda/lib/python3.11/site-packages/langchain_core/runnables/base.py:3022, in RunnableSequence.invoke(self, input, config, **kwargs)
3020 input = context.run(step.invoke, input, config, **kwargs)
3021 else:
-> 3022 input = context.run(step.invoke, input, config)
3023 # finish the root run
3024 except BaseException as e:

File /opt/conda/lib/python3.11/site-packages/langchain_core/runnables/base.py:4711, in RunnableLambda.invoke(self, input, config, **kwargs)
4697 """Invoke this Runnable synchronously.
4698
4699 Args:
(...)
4708 TypeError: If the Runnable is a coroutine function.
4709 """
4710 if hasattr(self, "func"):
-> 4711 return self._call_with_config(
4712 self._invoke,
4713 input,
4714 self._config(config, self.func),
4715 **kwargs,
4716 )
4717 else:
4718 msg = (
4719 "Cannot invoke a coroutine function synchronously."
4720 "Use ainvoke instead."
4721 )

File /opt/conda/lib/python3.11/site-packages/langchain_core/runnables/base.py:1925, in Runnable._call_with_config(self, func, input, config, run_type, serialized, **kwargs)
1921 context = copy_context()
1922 context.run(_set_config_context, child_config)
1923 output = cast(
1924 Output,
-> 1925 context.run(
1926 call_func_with_variable_args, # type: ignore[arg-type]
1927 func, # type: ignore[arg-type]
1928 input, # type: ignore[arg-type]
1929 config,
1930 run_manager,
1931 **kwargs,
1932 ),
1933 )
1934 except BaseException as e:
1935 run_manager.on_chain_error(e)

File /opt/conda/lib/python3.11/site-packages/langchain_core/runnables/config.py:396, in call_func_with_variable_args(func, input, config, run_manager, **kwargs)
394 if run_manager is not None and accepts_run_manager(func):
395 kwargs["run_manager"] = run_manager
--> 396 return func(input, **kwargs)

File /opt/conda/lib/python3.11/site-packages/langchain_core/runnables/base.py:4565, in RunnableLambda._invoke(self, input, run_manager, config, **kwargs)
4563 output = chunk
4564 else:
-> 4565 output = call_func_with_variable_args(
4566 self.func, input, config, run_manager, **kwargs
4567 )
4568 # If the output is a Runnable, invoke it
4569 if isinstance(output, Runnable):

File /opt/conda/lib/python3.11/site-packages/langchain_core/runnables/config.py:396, in call_func_with_variable_args(func, input, config, run_manager, **kwargs)
394 if run_manager is not None and accepts_run_manager(func):
395 kwargs["run_manager"] = run_manager
--> 396 return func(input, **kwargs)

File /opt/conda/lib/python3.11/site-packages/langchain_core/messages/utils.py:571, in merge_message_runs(messages, chunk_separator)
564 if (
565 isinstance(last_chunk.content, str)
566 and isinstance(curr_chunk.content, str)
567 and last_chunk.content
568 and curr_chunk.content
569 ):
570 last_chunk.content += chunk_separator
--> 571 merged.append(_chunk_to_msg(last_chunk + curr_chunk))
572 return merged

File /opt/conda/lib/python3.11/site-packages/langchain_core/messages/ai.py:395, in AIMessageChunk.add(self, other)
393 def add(self, other: Any) -> BaseMessageChunk: # type: ignore
394 if isinstance(other, AIMessageChunk):
--> 395 return add_ai_message_chunks(self, other)
396 elif isinstance(other, (list, tuple)) and all(
397 isinstance(o, AIMessageChunk) for o in other
398 ):
399 return add_ai_message_chunks(self, *other)

File /opt/conda/lib/python3.11/site-packages/langchain_core/messages/ai.py:412, in add_ai_message_chunks(left, *others)
409 raise ValueError(msg)
411 content = merge_content(left.content, *(o.content for o in others))
--> 412 additional_kwargs = merge_dicts(
413 left.additional_kwargs, *(o.additional_kwargs for o in others)
414 )
415 response_metadata = merge_dicts(
416 left.response_metadata, *(o.response_metadata for o in others)
417 )
419 # Merge tool call chunks

File /opt/conda/lib/python3.11/site-packages/langchain_core/utils/_merge.py:58, in merge_dicts(left, *others)
56 merged[right_k] += right_v
57 elif isinstance(merged[right_k], dict):
---> 58 merged[right_k] = merge_dicts(merged[right_k], right_v)
59 elif isinstance(merged[right_k], list):
60 merged[right_k] = merge_lists(merged[right_k], right_v)

File /opt/conda/lib/python3.11/site-packages/langchain_core/utils/_merge.py:68, in merge_dicts(left, *others)
63 else:
64 msg = (
65 f"Additional kwargs key {right_k} already exists in left dict and "
66 f"value has unsupported type {type(merged[right_k])}."
67 )
---> 68 raise TypeError(msg)
69 return merged

TypeError: Additional kwargs key prompt_tokens already exists in left dict and value has unsupported type <class 'int'>.

Description

I am trying to use the Pre built ReAct agent as nodes inside a LangGraph Graph. The graph as 2 ReAct based agent nodes.Both are exactly identical just with different prompts.

Agent 1 runs fine but Agent 2 gives the below error.

The issue seems to be in _merge.py utility within langchain_core library.
Similar issue with Gemini is discussed in #23827

Thanks in advance for all the help

System Info

System Information

OS: Linux
OS Version: #1 SMP Tue Dec 3 14:36:00 UTC 2024
Python Version: 3.11.9 | packaged by conda-forge | (main, Apr 19 2024, 18:36:13) [GCC 12.3.0]

Package Information

langchain_core: 0.3.29
langchain: 0.3.14
langchain_community: 0.3.14
langsmith: 0.2.7
langchain_aws: 0.2.10
langchain_chroma: 0.2.0
langchain_text_splitters: 0.3.4
langchainhub: 0.1.21
langgraph_sdk: 0.1.48

Optional packages not installed

langserve

Other Dependencies

aiohttp: 3.11.11
async-timeout: Installed. No version info available.
boto3: 1.35.91
chromadb: 0.5.23
dataclasses-json: 0.6.7
fastapi: 0.115.6
httpx: 0.27.0
httpx-sse: 0.4.0
jsonpatch: 1.33
langsmith-pyo3: Installed. No version info available.
numpy: 1.26.4
orjson: 3.10.13
packaging: 23.2
pydantic: 2.9.2
pydantic-settings: 2.7.1
PyYAML: 6.0.1
requests: 2.32.3
requests-toolbelt: 1.0.0
SQLAlchemy: 2.0.31
tenacity: 8.4.1
types-requests: 2.32.0.20241016
typing-extensions: 4.1

@dosubot dosubot bot added the 🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature label Jan 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature
Projects
None yet
Development

No branches or pull requests

1 participant