langchain 1.2.4__tar.gz → 1.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. {langchain-1.2.4 → langchain-1.2.5}/PKG-INFO +2 -2
  2. {langchain-1.2.4 → langchain-1.2.5}/langchain/__init__.py +1 -1
  3. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/summarization.py +61 -8
  4. {langchain-1.2.4 → langchain-1.2.5}/pyproject.toml +2 -2
  5. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_summarization.py +70 -0
  6. langchain-1.2.5/tests/unit_tests/agents/test_agent_name.py +219 -0
  7. {langchain-1.2.4 → langchain-1.2.5}/uv.lock +2 -2
  8. langchain-1.2.4/tests/unit_tests/agents/test_agent_name.py +0 -105
  9. {langchain-1.2.4 → langchain-1.2.5}/.gitignore +0 -0
  10. {langchain-1.2.4 → langchain-1.2.5}/LICENSE +0 -0
  11. {langchain-1.2.4 → langchain-1.2.5}/Makefile +0 -0
  12. {langchain-1.2.4 → langchain-1.2.5}/README.md +0 -0
  13. {langchain-1.2.4 → langchain-1.2.5}/extended_testing_deps.txt +0 -0
  14. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/__init__.py +0 -0
  15. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/factory.py +0 -0
  16. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/__init__.py +0 -0
  17. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/_execution.py +0 -0
  18. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/_redaction.py +0 -0
  19. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/_retry.py +0 -0
  20. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/context_editing.py +0 -0
  21. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/file_search.py +0 -0
  22. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/human_in_the_loop.py +0 -0
  23. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/model_call_limit.py +0 -0
  24. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/model_fallback.py +0 -0
  25. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/model_retry.py +0 -0
  26. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/pii.py +0 -0
  27. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/shell_tool.py +0 -0
  28. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/todo.py +0 -0
  29. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/tool_call_limit.py +0 -0
  30. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/tool_emulator.py +0 -0
  31. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/tool_retry.py +0 -0
  32. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/tool_selection.py +0 -0
  33. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/middleware/types.py +0 -0
  34. {langchain-1.2.4 → langchain-1.2.5}/langchain/agents/structured_output.py +0 -0
  35. {langchain-1.2.4 → langchain-1.2.5}/langchain/chat_models/__init__.py +0 -0
  36. {langchain-1.2.4 → langchain-1.2.5}/langchain/chat_models/base.py +0 -0
  37. {langchain-1.2.4 → langchain-1.2.5}/langchain/embeddings/__init__.py +0 -0
  38. {langchain-1.2.4 → langchain-1.2.5}/langchain/embeddings/base.py +0 -0
  39. {langchain-1.2.4 → langchain-1.2.5}/langchain/messages/__init__.py +0 -0
  40. {langchain-1.2.4 → langchain-1.2.5}/langchain/py.typed +0 -0
  41. {langchain-1.2.4 → langchain-1.2.5}/langchain/rate_limiters/__init__.py +0 -0
  42. {langchain-1.2.4 → langchain-1.2.5}/langchain/tools/__init__.py +0 -0
  43. {langchain-1.2.4 → langchain-1.2.5}/langchain/tools/tool_node.py +0 -0
  44. {langchain-1.2.4 → langchain-1.2.5}/scripts/check_imports.py +0 -0
  45. {langchain-1.2.4 → langchain-1.2.5}/tests/__init__.py +0 -0
  46. {langchain-1.2.4 → langchain-1.2.5}/tests/cassettes/test_inference_to_native_output[False].yaml.gz +0 -0
  47. {langchain-1.2.4 → langchain-1.2.5}/tests/cassettes/test_inference_to_native_output[True].yaml.gz +0 -0
  48. {langchain-1.2.4 → langchain-1.2.5}/tests/cassettes/test_inference_to_tool_output[False].yaml.gz +0 -0
  49. {langchain-1.2.4 → langchain-1.2.5}/tests/cassettes/test_inference_to_tool_output[True].yaml.gz +0 -0
  50. {langchain-1.2.4 → langchain-1.2.5}/tests/cassettes/test_strict_mode[False].yaml.gz +0 -0
  51. {langchain-1.2.4 → langchain-1.2.5}/tests/cassettes/test_strict_mode[True].yaml.gz +0 -0
  52. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/__init__.py +0 -0
  53. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/agents/__init__.py +0 -0
  54. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/agents/middleware/__init__.py +0 -0
  55. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/agents/middleware/test_shell_tool_integration.py +0 -0
  56. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/cache/__init__.py +0 -0
  57. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/cache/fake_embeddings.py +0 -0
  58. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/chat_models/__init__.py +0 -0
  59. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/chat_models/test_base.py +0 -0
  60. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/conftest.py +0 -0
  61. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/embeddings/__init__.py +0 -0
  62. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/embeddings/test_base.py +0 -0
  63. {langchain-1.2.4 → langchain-1.2.5}/tests/integration_tests/test_compile.py +0 -0
  64. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/__init__.py +0 -0
  65. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/__init__.py +0 -0
  66. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr +0 -0
  67. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/__snapshots__/test_middleware_decorators.ambr +0 -0
  68. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/__snapshots__/test_middleware_framework.ambr +0 -0
  69. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/__snapshots__/test_return_direct_graph.ambr +0 -0
  70. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/any_str.py +0 -0
  71. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/compose-postgres.yml +0 -0
  72. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/compose-redis.yml +0 -0
  73. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/conftest.py +0 -0
  74. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/conftest_checkpointer.py +0 -0
  75. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/conftest_store.py +0 -0
  76. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/memory_assert.py +0 -0
  77. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/messages.py +0 -0
  78. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/__init__.py +0 -0
  79. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/__snapshots__/test_middleware_decorators.ambr +0 -0
  80. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/__snapshots__/test_middleware_diagram.ambr +0 -0
  81. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/__snapshots__/test_middleware_framework.ambr +0 -0
  82. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/__init__.py +0 -0
  83. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/__snapshots__/test_decorators.ambr +0 -0
  84. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/__snapshots__/test_diagram.ambr +0 -0
  85. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/__snapshots__/test_framework.ambr +0 -0
  86. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_composition.py +0 -0
  87. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_decorators.py +0 -0
  88. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_diagram.py +0 -0
  89. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_framework.py +0 -0
  90. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_overrides.py +0 -0
  91. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_sync_async_wrappers.py +0 -0
  92. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_tools.py +0 -0
  93. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_wrap_model_call.py +0 -0
  94. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/core/test_wrap_tool_call.py +0 -0
  95. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/__init__.py +0 -0
  96. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_context_editing.py +0 -0
  97. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_file_search.py +0 -0
  98. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_human_in_the_loop.py +0 -0
  99. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_model_call_limit.py +0 -0
  100. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_model_fallback.py +0 -0
  101. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_model_retry.py +0 -0
  102. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_pii.py +0 -0
  103. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_shell_execution_policies.py +0 -0
  104. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_shell_tool.py +0 -0
  105. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_structured_output_retry.py +0 -0
  106. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_todo.py +0 -0
  107. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_tool_call_limit.py +0 -0
  108. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_tool_emulator.py +0 -0
  109. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_tool_retry.py +0 -0
  110. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/middleware/implementations/test_tool_selection.py +0 -0
  111. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/model.py +0 -0
  112. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/specifications/responses.json +0 -0
  113. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/specifications/return_direct.json +0 -0
  114. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_create_agent_tool_validation.py +0 -0
  115. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_injected_runtime_create_agent.py +0 -0
  116. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_kwargs_tool_runtime_injection.py +0 -0
  117. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_react_agent.py +0 -0
  118. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_response_format.py +0 -0
  119. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_response_format_integration.py +0 -0
  120. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_responses.py +0 -0
  121. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_responses_spec.py +0 -0
  122. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_return_direct_graph.py +0 -0
  123. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_return_direct_spec.py +0 -0
  124. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_state_schema.py +0 -0
  125. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/test_system_message.py +0 -0
  126. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/agents/utils.py +0 -0
  127. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/chat_models/__init__.py +0 -0
  128. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/chat_models/test_chat_models.py +0 -0
  129. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/conftest.py +0 -0
  130. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/embeddings/__init__.py +0 -0
  131. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/embeddings/test_base.py +0 -0
  132. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/embeddings/test_imports.py +0 -0
  133. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/test_dependencies.py +0 -0
  134. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/test_imports.py +0 -0
  135. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/test_pytest_config.py +0 -0
  136. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/test_version.py +0 -0
  137. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/tools/__init__.py +0 -0
  138. {langchain-1.2.4 → langchain-1.2.5}/tests/unit_tests/tools/test_imports.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain
3
- Version: 1.2.4
3
+ Version: 1.2.5
4
4
  Summary: Building applications with LLMs through composability
5
5
  Project-URL: Homepage, https://docs.langchain.com/
6
6
  Project-URL: Documentation, https://reference.langchain.com/python/langchain/langchain/
@@ -12,7 +12,7 @@ Project-URL: Reddit, https://www.reddit.com/r/LangChain/
12
12
  License: MIT
13
13
  License-File: LICENSE
14
14
  Requires-Python: <4.0.0,>=3.10.0
15
- Requires-Dist: langchain-core<2.0.0,>=1.2.1
15
+ Requires-Dist: langchain-core<2.0.0,>=1.2.7
16
16
  Requires-Dist: langgraph<1.1.0,>=1.0.2
17
17
  Requires-Dist: pydantic<3.0.0,>=2.7.4
18
18
  Provides-Extra: anthropic
@@ -1,3 +1,3 @@
1
1
  """Main entrypoint into LangChain."""
2
2
 
3
- __version__ = "1.2.4"
3
+ __version__ = "1.2.5"
@@ -19,6 +19,8 @@ from langchain_core.messages.utils import (
19
19
  get_buffer_string,
20
20
  trim_messages,
21
21
  )
22
+ from langchain_core.runnables.config import RunnableConfig, merge_configs
23
+ from langgraph.config import get_config
22
24
  from langgraph.graph.message import (
23
25
  REMOVE_ALL_MESSAGES,
24
26
  )
@@ -40,12 +42,27 @@ Your sole objective in this task is to extract the highest quality/most relevant
40
42
 
41
43
  <objective_information>
42
44
  You're nearing the total number of input tokens you can accept, so you must extract the highest quality/most relevant pieces of information from your conversation history.
43
- This context will then overwrite the conversation history presented below. Because of this, ensure the context you extract is only the most important information to your overall goal.
45
+ This context will then overwrite the conversation history presented below. Because of this, ensure the context you extract is only the most important information to continue working toward your overall goal.
44
46
  </objective_information>
45
47
 
46
48
  <instructions>
47
- The conversation history below will be replaced with the context you extract in this step. Because of this, you must do your very best to extract and record all of the most important context from the conversation history.
49
+ The conversation history below will be replaced with the context you extract in this step.
48
50
  You want to ensure that you don't repeat any actions you've already completed, so the context you extract from the conversation history should be focused on the most important information to your overall goal.
51
+
52
+ You should structure your summary using the following sections. Each section acts as a checklist - you must populate it with relevant information or explicitly state "None" if there is nothing to report for that section:
53
+
54
+ ## SESSION INTENT
55
+ What is the user's primary goal or request? What overall task are you trying to accomplish? This should be concise but complete enough to understand the purpose of the entire session.
56
+
57
+ ## SUMMARY
58
+ Extract and record all of the most important context from the conversation history. Include important choices, conclusions, or strategies determined during this conversation. Include the reasoning behind key decisions. Document any rejected options and why they were not pursued.
59
+
60
+ ## ARTIFACTS
61
+ What artifacts, files, or resources were created, modified, or accessed during this conversation? For file modifications, list specific file paths and briefly describe the changes made to each. This section prevents silent loss of artifact information.
62
+
63
+ ## NEXT STEPS
64
+ What specific tasks remain to be completed to achieve the session intent? What should you do next?
65
+
49
66
  </instructions>
50
67
 
51
68
  The user will message you with the full message history you'll be extracting context from, to then replace. Carefully read over it all, and think deeply about what information is most important to your overall goal that should be saved:
@@ -269,7 +286,7 @@ class SummarizationMiddleware(AgentMiddleware):
269
286
  raise ValueError(msg)
270
287
 
271
288
  @override
272
- def before_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
289
+ def before_model(self, state: AgentState[Any], _runtime: Runtime) -> dict[str, Any] | None:
273
290
  """Process messages before model invocation, potentially triggering summarization.
274
291
 
275
292
  Args:
@@ -306,7 +323,7 @@ class SummarizationMiddleware(AgentMiddleware):
306
323
 
307
324
  @override
308
325
  async def abefore_model(
309
- self, state: AgentState[Any], runtime: Runtime
326
+ self, state: AgentState[Any], _runtime: Runtime
310
327
  ) -> dict[str, Any] | None:
311
328
  """Process messages before model invocation, potentially triggering summarization.
312
329
 
@@ -563,7 +580,11 @@ class SummarizationMiddleware(AgentMiddleware):
563
580
  return idx
564
581
 
565
582
  def _create_summary(self, messages_to_summarize: list[AnyMessage]) -> str:
566
- """Generate summary for the given messages."""
583
+ """Generate summary for the given messages.
584
+
585
+ Args:
586
+ messages_to_summarize: Messages to summarize.
587
+ """
567
588
  if not messages_to_summarize:
568
589
  return "No previous conversation history."
569
590
 
@@ -575,14 +596,33 @@ class SummarizationMiddleware(AgentMiddleware):
575
596
  # message objects
576
597
  formatted_messages = get_buffer_string(trimmed_messages)
577
598
 
599
+ # Merge parent config with summarization metadata.
600
+ # Use get_config() to get the current LangGraph config which contains
601
+ # langgraph_checkpoint_ns - required by StreamMessagesHandler to properly
602
+ # track the model call and propagate metadata (including lc_source) to
603
+ # stream chunks.
578
604
  try:
579
- response = self.model.invoke(self.summary_prompt.format(messages=formatted_messages))
605
+ base_config: RunnableConfig = get_config()
606
+ except RuntimeError:
607
+ # Fallback if called outside a runnable context
608
+ base_config = {}
609
+ config = merge_configs(base_config, {"metadata": {"lc_source": "summarization"}})
610
+
611
+ try:
612
+ response = self.model.invoke(
613
+ self.summary_prompt.format(messages=formatted_messages),
614
+ config=config,
615
+ )
580
616
  return response.text.strip()
581
617
  except Exception as e:
582
618
  return f"Error generating summary: {e!s}"
583
619
 
584
620
  async def _acreate_summary(self, messages_to_summarize: list[AnyMessage]) -> str:
585
- """Generate summary for the given messages."""
621
+ """Generate summary for the given messages.
622
+
623
+ Args:
624
+ messages_to_summarize: Messages to summarize.
625
+ """
586
626
  if not messages_to_summarize:
587
627
  return "No previous conversation history."
588
628
 
@@ -594,9 +634,22 @@ class SummarizationMiddleware(AgentMiddleware):
594
634
  # message objects
595
635
  formatted_messages = get_buffer_string(trimmed_messages)
596
636
 
637
+ # Merge parent config with summarization metadata.
638
+ # Use get_config() to get the current LangGraph config which contains
639
+ # langgraph_checkpoint_ns - required by StreamMessagesHandler to properly
640
+ # track the model call and propagate metadata (including lc_source) to
641
+ # stream chunks.
642
+ try:
643
+ base_config: RunnableConfig = get_config()
644
+ except RuntimeError:
645
+ # Fallback if called outside a runnable context
646
+ base_config = {}
647
+ config = merge_configs(base_config, {"metadata": {"lc_source": "summarization"}})
648
+
597
649
  try:
598
650
  response = await self.model.ainvoke(
599
- self.summary_prompt.format(messages=formatted_messages)
651
+ self.summary_prompt.format(messages=formatted_messages),
652
+ config=config,
600
653
  )
601
654
  return response.text.strip()
602
655
  except Exception as e:
@@ -9,10 +9,10 @@ license = { text = "MIT" }
9
9
  readme = "README.md"
10
10
  authors = []
11
11
 
12
- version = "1.2.4"
12
+ version = "1.2.5"
13
13
  requires-python = ">=3.10.0,<4.0.0"
14
14
  dependencies = [
15
- "langchain-core>=1.2.1,<2.0.0",
15
+ "langchain-core>=1.2.7,<2.0.0",
16
16
  "langgraph>=1.0.2,<1.1.0",
17
17
  "pydantic>=2.7.4,<3.0.0",
18
18
  ]
@@ -1214,3 +1214,73 @@ def test_usage_metadata_trigger() -> None:
1214
1214
  ]
1215
1215
  )
1216
1216
  assert not middleware._should_summarize(messages, 0)
1217
+
1218
+
1219
+ class ConfigCapturingModel(BaseChatModel):
1220
+ """Mock model that captures the config passed to invoke/ainvoke."""
1221
+
1222
+ captured_configs: list[RunnableConfig | None] = Field(default_factory=list, exclude=True)
1223
+
1224
+ @override
1225
+ def invoke(
1226
+ self,
1227
+ input: LanguageModelInput,
1228
+ config: RunnableConfig | None = None,
1229
+ *,
1230
+ stop: list[str] | None = None,
1231
+ **kwargs: Any,
1232
+ ) -> AIMessage:
1233
+ self.captured_configs.append(config)
1234
+ return AIMessage(content="Summary")
1235
+
1236
+ @override
1237
+ async def ainvoke(
1238
+ self,
1239
+ input: LanguageModelInput,
1240
+ config: RunnableConfig | None = None,
1241
+ *,
1242
+ stop: list[str] | None = None,
1243
+ **kwargs: Any,
1244
+ ) -> AIMessage:
1245
+ self.captured_configs.append(config)
1246
+ return AIMessage(content="Summary")
1247
+
1248
+ @override
1249
+ def _generate(
1250
+ self,
1251
+ messages: list[BaseMessage],
1252
+ stop: list[str] | None = None,
1253
+ run_manager: CallbackManagerForLLMRun | None = None,
1254
+ **kwargs: Any,
1255
+ ) -> ChatResult:
1256
+ return ChatResult(generations=[ChatGeneration(message=AIMessage(content="Summary"))])
1257
+
1258
+ @property
1259
+ def _llm_type(self) -> str:
1260
+ return "config-capturing"
1261
+
1262
+
1263
+ @pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"])
1264
+ async def test_create_summary_passes_lc_source_metadata(use_async: bool) -> None: # noqa: FBT001
1265
+ """Test that summary creation passes `lc_source` metadata to the model.
1266
+
1267
+ When called outside a LangGraph runnable context, `get_config()` raises
1268
+ `RuntimeError`. The middleware catches this and still passes the `lc_source`
1269
+ metadata to the model.
1270
+ """
1271
+ model = ConfigCapturingModel()
1272
+ model.captured_configs = [] # Reset for this test
1273
+ middleware = SummarizationMiddleware(model=model, trigger=("tokens", 1000))
1274
+ messages: list[AnyMessage] = [HumanMessage(content="Hello"), AIMessage(content="Hi")]
1275
+
1276
+ if use_async:
1277
+ summary = await middleware._acreate_summary(messages)
1278
+ else:
1279
+ summary = middleware._create_summary(messages)
1280
+
1281
+ assert summary == "Summary"
1282
+ assert len(model.captured_configs) == 1
1283
+ config = model.captured_configs[0]
1284
+ assert config is not None
1285
+ assert "metadata" in config
1286
+ assert config["metadata"]["lc_source"] == "summarization"
@@ -0,0 +1,219 @@
1
+ """Test agent name parameter in create_agent.
2
+
3
+ This module tests that the name parameter correctly sets .name on AIMessage outputs.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from langchain_core.messages import (
9
+ AIMessage,
10
+ HumanMessage,
11
+ ToolCall,
12
+ )
13
+ from langchain_core.tools import tool
14
+
15
+ from langchain.agents import create_agent
16
+ from tests.unit_tests.agents.model import FakeToolCallingModel
17
+
18
+
19
+ @tool
20
+ def simple_tool(x: int) -> str:
21
+ """Simple tool for basic tests."""
22
+ return f"Result: {x}"
23
+
24
+
25
+ def test_agent_name_set_on_ai_message() -> None:
26
+ """Test that agent name is set on AIMessage when name is provided."""
27
+ tool_calls: list[list[ToolCall]] = [[]]
28
+ agent = create_agent(
29
+ model=FakeToolCallingModel(tool_calls=tool_calls),
30
+ name="test_agent",
31
+ )
32
+
33
+ result = agent.invoke({"messages": [HumanMessage("Hello")]})
34
+
35
+ ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
36
+ assert len(ai_messages) == 1
37
+ assert ai_messages[0].name == "test_agent"
38
+
39
+
40
+ def test_agent_name_not_set_when_none() -> None:
41
+ """Test that AIMessage.name is not set when name is not provided."""
42
+ tool_calls: list[list[ToolCall]] = [[]]
43
+ agent = create_agent(
44
+ model=FakeToolCallingModel(tool_calls=tool_calls),
45
+ )
46
+
47
+ result = agent.invoke({"messages": [HumanMessage("Hello")]})
48
+
49
+ ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
50
+ assert len(ai_messages) == 1
51
+ assert ai_messages[0].name is None
52
+
53
+
54
+ def test_agent_name_on_multiple_iterations() -> None:
55
+ """Test that agent name is set on all AIMessages in multi-turn conversation."""
56
+ agent = create_agent(
57
+ model=FakeToolCallingModel(
58
+ tool_calls=[[{"args": {"x": 1}, "id": "call_1", "name": "simple_tool"}], []]
59
+ ),
60
+ tools=[simple_tool],
61
+ name="multi_turn_agent",
62
+ )
63
+
64
+ result = agent.invoke({"messages": [HumanMessage("Call a tool")]})
65
+
66
+ ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
67
+ assert len(ai_messages) == 2
68
+ for msg in ai_messages:
69
+ assert msg.name == "multi_turn_agent"
70
+
71
+
72
+ async def test_agent_name_async() -> None:
73
+ """Test that agent name is set on AIMessage in async execution."""
74
+ tool_calls: list[list[ToolCall]] = [[]]
75
+ agent = create_agent(
76
+ model=FakeToolCallingModel(tool_calls=tool_calls),
77
+ name="async_agent",
78
+ )
79
+
80
+ result = await agent.ainvoke({"messages": [HumanMessage("Hello async")]})
81
+
82
+ ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
83
+ assert len(ai_messages) == 1
84
+ assert ai_messages[0].name == "async_agent"
85
+
86
+
87
+ async def test_agent_name_async_multiple_iterations() -> None:
88
+ """Test that agent name is set on all AIMessages in async multi-turn."""
89
+ agent = create_agent(
90
+ model=FakeToolCallingModel(
91
+ tool_calls=[[{"args": {"x": 5}, "id": "call_1", "name": "simple_tool"}], []]
92
+ ),
93
+ tools=[simple_tool],
94
+ name="async_multi_agent",
95
+ )
96
+
97
+ result = await agent.ainvoke({"messages": [HumanMessage("Call tool async")]})
98
+
99
+ ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
100
+ assert len(ai_messages) == 2
101
+ for msg in ai_messages:
102
+ assert msg.name == "async_multi_agent"
103
+
104
+
105
+ # Tests for lc_agent_name in streaming metadata
106
+
107
+
108
+ def test_lc_agent_name_in_stream_metadata() -> None:
109
+ """Test that lc_agent_name is included in metadata when streaming with name."""
110
+ tool_calls: list[list[ToolCall]] = [[]]
111
+ agent = create_agent(
112
+ model=FakeToolCallingModel(tool_calls=tool_calls),
113
+ name="streaming_agent",
114
+ )
115
+
116
+ metadata_with_agent_name = []
117
+ for _chunk, metadata in agent.stream(
118
+ {"messages": [HumanMessage("Hello")]},
119
+ stream_mode="messages",
120
+ ):
121
+ if "lc_agent_name" in metadata:
122
+ metadata_with_agent_name.append(metadata["lc_agent_name"])
123
+
124
+ assert len(metadata_with_agent_name) > 0
125
+ assert all(name == "streaming_agent" for name in metadata_with_agent_name)
126
+
127
+
128
+ def test_lc_agent_name_not_in_stream_metadata_when_name_not_provided() -> None:
129
+ """Test that lc_agent_name is not in metadata when name is not provided."""
130
+ tool_calls: list[list[ToolCall]] = [[]]
131
+ agent = create_agent(
132
+ model=FakeToolCallingModel(tool_calls=tool_calls),
133
+ )
134
+
135
+ for _chunk, metadata in agent.stream(
136
+ {"messages": [HumanMessage("Hello")]},
137
+ stream_mode="messages",
138
+ ):
139
+ assert "lc_agent_name" not in metadata
140
+
141
+
142
+ def test_lc_agent_name_in_stream_metadata_multiple_iterations() -> None:
143
+ """Test that lc_agent_name is in metadata for all stream events in multi-turn."""
144
+ agent = create_agent(
145
+ model=FakeToolCallingModel(
146
+ tool_calls=[[{"args": {"x": 1}, "id": "call_1", "name": "simple_tool"}], []]
147
+ ),
148
+ tools=[simple_tool],
149
+ name="multi_turn_streaming_agent",
150
+ )
151
+
152
+ metadata_with_agent_name = []
153
+ for _chunk, metadata in agent.stream(
154
+ {"messages": [HumanMessage("Call a tool")]},
155
+ stream_mode="messages",
156
+ ):
157
+ if "lc_agent_name" in metadata:
158
+ metadata_with_agent_name.append(metadata["lc_agent_name"])
159
+
160
+ # Should have metadata entries for messages from both iterations
161
+ assert len(metadata_with_agent_name) > 0
162
+ assert all(name == "multi_turn_streaming_agent" for name in metadata_with_agent_name)
163
+
164
+
165
+ async def test_lc_agent_name_in_astream_metadata() -> None:
166
+ """Test that lc_agent_name is included in metadata when async streaming with name."""
167
+ tool_calls: list[list[ToolCall]] = [[]]
168
+ agent = create_agent(
169
+ model=FakeToolCallingModel(tool_calls=tool_calls),
170
+ name="async_streaming_agent",
171
+ )
172
+
173
+ metadata_with_agent_name = []
174
+ async for _chunk, metadata in agent.astream(
175
+ {"messages": [HumanMessage("Hello async")]},
176
+ stream_mode="messages",
177
+ ):
178
+ if "lc_agent_name" in metadata:
179
+ metadata_with_agent_name.append(metadata["lc_agent_name"])
180
+
181
+ assert len(metadata_with_agent_name) > 0
182
+ assert all(name == "async_streaming_agent" for name in metadata_with_agent_name)
183
+
184
+
185
+ async def test_lc_agent_name_not_in_astream_metadata_when_name_not_provided() -> None:
186
+ """Test that lc_agent_name is not in async stream metadata when name not provided."""
187
+ tool_calls: list[list[ToolCall]] = [[]]
188
+ agent = create_agent(
189
+ model=FakeToolCallingModel(tool_calls=tool_calls),
190
+ )
191
+
192
+ async for _chunk, metadata in agent.astream(
193
+ {"messages": [HumanMessage("Hello async")]},
194
+ stream_mode="messages",
195
+ ):
196
+ assert "lc_agent_name" not in metadata
197
+
198
+
199
+ async def test_lc_agent_name_in_astream_metadata_multiple_iterations() -> None:
200
+ """Test that lc_agent_name is in metadata for all async stream events in multi-turn."""
201
+ agent = create_agent(
202
+ model=FakeToolCallingModel(
203
+ tool_calls=[[{"args": {"x": 5}, "id": "call_1", "name": "simple_tool"}], []]
204
+ ),
205
+ tools=[simple_tool],
206
+ name="async_multi_turn_streaming_agent",
207
+ )
208
+
209
+ metadata_with_agent_name = []
210
+ async for _chunk, metadata in agent.astream(
211
+ {"messages": [HumanMessage("Call tool async")]},
212
+ stream_mode="messages",
213
+ ):
214
+ if "lc_agent_name" in metadata:
215
+ metadata_with_agent_name.append(metadata["lc_agent_name"])
216
+
217
+ # Should have metadata entries for messages from both iterations
218
+ assert len(metadata_with_agent_name) > 0
219
+ assert all(name == "async_multi_turn_streaming_agent" for name in metadata_with_agent_name)
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 2
3
3
  requires-python = ">=3.10.0, <4.0.0"
4
4
  resolution-markers = [
5
5
  "python_full_version >= '3.14' and platform_python_implementation == 'PyPy'",
@@ -1863,7 +1863,7 @@ wheels = [
1863
1863
 
1864
1864
  [[package]]
1865
1865
  name = "langchain"
1866
- version = "1.2.4"
1866
+ version = "1.2.5"
1867
1867
  source = { editable = "." }
1868
1868
  dependencies = [
1869
1869
  { name = "langchain-core" },
@@ -1,105 +0,0 @@
1
- """Test agent name parameter in create_agent.
2
-
3
- This module tests that the name parameter correctly sets .name on AIMessage outputs.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- import pytest
9
- from langchain_core.messages import (
10
- AIMessage,
11
- HumanMessage,
12
- ToolCall,
13
- )
14
- from langchain_core.tools import tool
15
-
16
- from langchain.agents import create_agent
17
- from tests.unit_tests.agents.model import FakeToolCallingModel
18
-
19
-
20
- @tool
21
- def simple_tool(x: int) -> str:
22
- """Simple tool for basic tests."""
23
- return f"Result: {x}"
24
-
25
-
26
- def test_agent_name_set_on_ai_message() -> None:
27
- """Test that agent name is set on AIMessage when name is provided."""
28
- tool_calls: list[list[ToolCall]] = [[]]
29
- agent = create_agent(
30
- model=FakeToolCallingModel(tool_calls=tool_calls),
31
- name="test_agent",
32
- )
33
-
34
- result = agent.invoke({"messages": [HumanMessage("Hello")]})
35
-
36
- ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
37
- assert len(ai_messages) == 1
38
- assert ai_messages[0].name == "test_agent"
39
-
40
-
41
- def test_agent_name_not_set_when_none() -> None:
42
- """Test that AIMessage.name is not set when name is not provided."""
43
- tool_calls: list[list[ToolCall]] = [[]]
44
- agent = create_agent(
45
- model=FakeToolCallingModel(tool_calls=tool_calls),
46
- )
47
-
48
- result = agent.invoke({"messages": [HumanMessage("Hello")]})
49
-
50
- ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
51
- assert len(ai_messages) == 1
52
- assert ai_messages[0].name is None
53
-
54
-
55
- def test_agent_name_on_multiple_iterations() -> None:
56
- """Test that agent name is set on all AIMessages in multi-turn conversation."""
57
- agent = create_agent(
58
- model=FakeToolCallingModel(
59
- tool_calls=[[{"args": {"x": 1}, "id": "call_1", "name": "simple_tool"}], []]
60
- ),
61
- tools=[simple_tool],
62
- name="multi_turn_agent",
63
- )
64
-
65
- result = agent.invoke({"messages": [HumanMessage("Call a tool")]})
66
-
67
- ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
68
- assert len(ai_messages) == 2
69
- for msg in ai_messages:
70
- assert msg.name == "multi_turn_agent"
71
-
72
-
73
- @pytest.mark.asyncio
74
- async def test_agent_name_async() -> None:
75
- """Test that agent name is set on AIMessage in async execution."""
76
- tool_calls: list[list[ToolCall]] = [[]]
77
- agent = create_agent(
78
- model=FakeToolCallingModel(tool_calls=tool_calls),
79
- name="async_agent",
80
- )
81
-
82
- result = await agent.ainvoke({"messages": [HumanMessage("Hello async")]})
83
-
84
- ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
85
- assert len(ai_messages) == 1
86
- assert ai_messages[0].name == "async_agent"
87
-
88
-
89
- @pytest.mark.asyncio
90
- async def test_agent_name_async_multiple_iterations() -> None:
91
- """Test that agent name is set on all AIMessages in async multi-turn."""
92
- agent = create_agent(
93
- model=FakeToolCallingModel(
94
- tool_calls=[[{"args": {"x": 5}, "id": "call_1", "name": "simple_tool"}], []]
95
- ),
96
- tools=[simple_tool],
97
- name="async_multi_agent",
98
- )
99
-
100
- result = await agent.ainvoke({"messages": [HumanMessage("Call tool async")]})
101
-
102
- ai_messages = [m for m in result["messages"] if isinstance(m, AIMessage)]
103
- assert len(ai_messages) == 2
104
- for msg in ai_messages:
105
- assert msg.name == "async_multi_agent"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes