langchain-core 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl

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 (53) hide show
  1. langchain_core/agents.py +36 -27
  2. langchain_core/callbacks/base.py +1 -2
  3. langchain_core/callbacks/manager.py +19 -2
  4. langchain_core/callbacks/usage.py +2 -2
  5. langchain_core/documents/base.py +6 -6
  6. langchain_core/example_selectors/length_based.py +1 -1
  7. langchain_core/indexing/api.py +17 -14
  8. langchain_core/language_models/_utils.py +1 -1
  9. langchain_core/language_models/base.py +50 -20
  10. langchain_core/language_models/chat_models.py +48 -29
  11. langchain_core/language_models/llms.py +66 -36
  12. langchain_core/load/load.py +15 -9
  13. langchain_core/messages/ai.py +3 -3
  14. langchain_core/messages/base.py +4 -3
  15. langchain_core/messages/block_translators/__init__.py +2 -1
  16. langchain_core/messages/block_translators/openai.py +2 -1
  17. langchain_core/messages/content.py +2 -2
  18. langchain_core/messages/utils.py +12 -8
  19. langchain_core/output_parsers/openai_tools.py +14 -2
  20. langchain_core/outputs/chat_generation.py +4 -2
  21. langchain_core/outputs/generation.py +6 -5
  22. langchain_core/prompt_values.py +2 -2
  23. langchain_core/prompts/base.py +50 -45
  24. langchain_core/prompts/chat.py +35 -28
  25. langchain_core/prompts/dict.py +1 -1
  26. langchain_core/prompts/message.py +5 -5
  27. langchain_core/prompts/string.py +4 -2
  28. langchain_core/runnables/base.py +97 -52
  29. langchain_core/runnables/branch.py +22 -20
  30. langchain_core/runnables/configurable.py +30 -29
  31. langchain_core/runnables/fallbacks.py +22 -20
  32. langchain_core/runnables/graph.py +1 -2
  33. langchain_core/runnables/graph_ascii.py +2 -1
  34. langchain_core/runnables/graph_mermaid.py +4 -1
  35. langchain_core/runnables/graph_png.py +28 -0
  36. langchain_core/runnables/history.py +43 -32
  37. langchain_core/runnables/passthrough.py +35 -25
  38. langchain_core/runnables/router.py +5 -5
  39. langchain_core/runnables/schema.py +1 -1
  40. langchain_core/runnables/utils.py +3 -2
  41. langchain_core/sys_info.py +4 -2
  42. langchain_core/tools/base.py +22 -16
  43. langchain_core/tracers/core.py +6 -6
  44. langchain_core/utils/function_calling.py +11 -7
  45. langchain_core/utils/input.py +3 -0
  46. langchain_core/utils/json.py +4 -2
  47. langchain_core/utils/pydantic.py +5 -4
  48. langchain_core/vectorstores/base.py +1 -2
  49. langchain_core/vectorstores/in_memory.py +1 -2
  50. langchain_core/version.py +1 -1
  51. {langchain_core-1.0.3.dist-info → langchain_core-1.0.5.dist-info}/METADATA +2 -2
  52. {langchain_core-1.0.3.dist-info → langchain_core-1.0.5.dist-info}/RECORD +53 -53
  53. {langchain_core-1.0.3.dist-info → langchain_core-1.0.5.dist-info}/WHEEL +0 -0
@@ -35,20 +35,20 @@ if TYPE_CHECKING:
35
35
 
36
36
 
37
37
  class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
38
- """Runnable that can fallback to other Runnables if it fails.
38
+ """`Runnable` that can fallback to other `Runnable`s if it fails.
39
39
 
40
40
  External APIs (e.g., APIs for a language model) may at times experience
41
41
  degraded performance or even downtime.
42
42
 
43
- In these cases, it can be useful to have a fallback Runnable that can be
44
- used in place of the original Runnable (e.g., fallback to another LLM provider).
43
+ In these cases, it can be useful to have a fallback `Runnable` that can be
44
+ used in place of the original `Runnable` (e.g., fallback to another LLM provider).
45
45
 
46
- Fallbacks can be defined at the level of a single Runnable, or at the level
47
- of a chain of Runnables. Fallbacks are tried in order until one succeeds or
46
+ Fallbacks can be defined at the level of a single `Runnable`, or at the level
47
+ of a chain of `Runnable`s. Fallbacks are tried in order until one succeeds or
48
48
  all fail.
49
49
 
50
50
  While you can instantiate a `RunnableWithFallbacks` directly, it is usually
51
- more convenient to use the `with_fallbacks` method on a Runnable.
51
+ more convenient to use the `with_fallbacks` method on a `Runnable`.
52
52
 
53
53
  Example:
54
54
  ```python
@@ -87,7 +87,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
87
87
  """
88
88
 
89
89
  runnable: Runnable[Input, Output]
90
- """The Runnable to run first."""
90
+ """The `Runnable` to run first."""
91
91
  fallbacks: Sequence[Runnable[Input, Output]]
92
92
  """A sequence of fallbacks to try."""
93
93
  exceptions_to_handle: tuple[type[BaseException], ...] = (Exception,)
@@ -97,9 +97,12 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
97
97
  """
98
98
  exception_key: str | None = None
99
99
  """If `string` is specified then handled exceptions will be passed to fallbacks as
100
- part of the input under the specified key. If `None`, exceptions
101
- will not be passed to fallbacks. If used, the base Runnable and its fallbacks
102
- must accept a dictionary as input."""
100
+ part of the input under the specified key.
101
+
102
+ If `None`, exceptions will not be passed to fallbacks.
103
+
104
+ If used, the base `Runnable` and its fallbacks must accept a dictionary as input.
105
+ """
103
106
 
104
107
  model_config = ConfigDict(
105
108
  arbitrary_types_allowed=True,
@@ -137,7 +140,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
137
140
  @classmethod
138
141
  @override
139
142
  def is_lc_serializable(cls) -> bool:
140
- """Return True as this class is serializable."""
143
+ """Return `True` as this class is serializable."""
141
144
  return True
142
145
 
143
146
  @classmethod
@@ -152,10 +155,10 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
152
155
 
153
156
  @property
154
157
  def runnables(self) -> Iterator[Runnable[Input, Output]]:
155
- """Iterator over the Runnable and its fallbacks.
158
+ """Iterator over the `Runnable` and its fallbacks.
156
159
 
157
160
  Yields:
158
- The Runnable then its fallbacks.
161
+ The `Runnable` then its fallbacks.
159
162
  """
160
163
  yield self.runnable
161
164
  yield from self.fallbacks
@@ -589,14 +592,14 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
589
592
  await run_manager.on_chain_end(output)
590
593
 
591
594
  def __getattr__(self, name: str) -> Any:
592
- """Get an attribute from the wrapped Runnable and its fallbacks.
595
+ """Get an attribute from the wrapped `Runnable` and its fallbacks.
593
596
 
594
597
  Returns:
595
- If the attribute is anything other than a method that outputs a Runnable,
596
- returns getattr(self.runnable, name). If the attribute is a method that
597
- does return a new Runnable (e.g. model.bind_tools([...]) outputs a new
598
- RunnableBinding) then self.runnable and each of the runnables in
599
- self.fallbacks is replaced with getattr(x, name).
598
+ If the attribute is anything other than a method that outputs a `Runnable`,
599
+ returns `getattr(self.runnable, name)`. If the attribute is a method that
600
+ does return a new `Runnable` (e.g. `model.bind_tools([...])` outputs a new
601
+ `RunnableBinding`) then `self.runnable` and each of the runnables in
602
+ `self.fallbacks` is replaced with `getattr(x, name)`.
600
603
 
601
604
  Example:
602
605
  ```python
@@ -618,7 +621,6 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
618
621
  runnable=RunnableBinding(bound=ChatOpenAI(...), kwargs={"tools": [...]}),
619
622
  fallbacks=[RunnableBinding(bound=ChatAnthropic(...), kwargs={"tools": [...]})],
620
623
  )
621
-
622
624
  ```
623
625
  """ # noqa: E501
624
626
  attr = getattr(self.runnable, name)
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import inspect
6
6
  from collections import defaultdict
7
- from collections.abc import Callable
8
7
  from dataclasses import dataclass, field
9
8
  from enum import Enum
10
9
  from typing import (
@@ -22,7 +21,7 @@ from langchain_core.runnables.base import Runnable, RunnableSerializable
22
21
  from langchain_core.utils.pydantic import _IgnoreUnserializable, is_basemodel_subclass
23
22
 
24
23
  if TYPE_CHECKING:
25
- from collections.abc import Sequence
24
+ from collections.abc import Callable, Sequence
26
25
 
27
26
  from pydantic import BaseModel
28
27
 
@@ -7,7 +7,6 @@ from __future__ import annotations
7
7
 
8
8
  import math
9
9
  import os
10
- from collections.abc import Mapping, Sequence
11
10
  from typing import TYPE_CHECKING, Any
12
11
 
13
12
  try:
@@ -20,6 +19,8 @@ except ImportError:
20
19
  _HAS_GRANDALF = False
21
20
 
22
21
  if TYPE_CHECKING:
22
+ from collections.abc import Mapping, Sequence
23
+
23
24
  from langchain_core.runnables.graph import Edge as LangEdge
24
25
 
25
26
 
@@ -454,7 +454,10 @@ def _render_mermaid_using_api(
454
454
  return img_bytes
455
455
 
456
456
  # If we get a server error (5xx), retry
457
- if 500 <= response.status_code < 600 and attempt < max_retries:
457
+ if (
458
+ requests.codes.internal_server_error <= response.status_code
459
+ and attempt < max_retries
460
+ ):
458
461
  # Exponential backoff with jitter
459
462
  sleep_time = retry_delay * (2**attempt) * (0.5 + 0.5 * random.random()) # noqa: S311 not used for crypto
460
463
  time.sleep(sleep_time)
@@ -1,5 +1,6 @@
1
1
  """Helper class to draw a state graph into a PNG file."""
2
2
 
3
+ from itertools import groupby
3
4
  from typing import Any
4
5
 
5
6
  from langchain_core.runnables.graph import Graph, LabelsDict
@@ -141,6 +142,7 @@ class PngDrawer:
141
142
  # Add nodes, conditional edges, and edges to the graph
142
143
  self.add_nodes(viz, graph)
143
144
  self.add_edges(viz, graph)
145
+ self.add_subgraph(viz, [node.split(":") for node in graph.nodes])
144
146
 
145
147
  # Update entrypoint and END styles
146
148
  self.update_styles(viz, graph)
@@ -161,6 +163,32 @@ class PngDrawer:
161
163
  for node in graph.nodes:
162
164
  self.add_node(viz, node)
163
165
 
166
+ def add_subgraph(
167
+ self,
168
+ viz: Any,
169
+ nodes: list[list[str]],
170
+ parent_prefix: list[str] | None = None,
171
+ ) -> None:
172
+ """Add subgraphs to the graph.
173
+
174
+ Args:
175
+ viz: The graphviz object.
176
+ nodes: The nodes to add.
177
+ parent_prefix: The prefix of the parent subgraph.
178
+ """
179
+ for prefix, grouped in groupby(
180
+ [node[:] for node in sorted(nodes)],
181
+ key=lambda x: x.pop(0),
182
+ ):
183
+ current_prefix = (parent_prefix or []) + [prefix]
184
+ grouped_nodes = list(grouped)
185
+ if len(grouped_nodes) > 1:
186
+ subgraph = viz.add_subgraph(
187
+ [":".join(current_prefix + node) for node in grouped_nodes],
188
+ name="cluster_" + ":".join(current_prefix),
189
+ )
190
+ self.add_subgraph(subgraph, grouped_nodes, current_prefix)
191
+
164
192
  def add_edges(self, viz: Any, graph: Graph) -> None:
165
193
  """Add edges to the graph.
166
194
 
@@ -36,23 +36,23 @@ GetSessionHistoryCallable = Callable[..., BaseChatMessageHistory]
36
36
 
37
37
 
38
38
  class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
39
- """Runnable that manages chat message history for another Runnable.
39
+ """`Runnable` that manages chat message history for another `Runnable`.
40
40
 
41
41
  A chat message history is a sequence of messages that represent a conversation.
42
42
 
43
- RunnableWithMessageHistory wraps another Runnable and manages the chat message
43
+ `RunnableWithMessageHistory` wraps another `Runnable` and manages the chat message
44
44
  history for it; it is responsible for reading and updating the chat message
45
45
  history.
46
46
 
47
- The formats supported for the inputs and outputs of the wrapped Runnable
47
+ The formats supported for the inputs and outputs of the wrapped `Runnable`
48
48
  are described below.
49
49
 
50
- RunnableWithMessageHistory must always be called with a config that contains
50
+ `RunnableWithMessageHistory` must always be called with a config that contains
51
51
  the appropriate parameters for the chat message history factory.
52
52
 
53
- By default, the Runnable is expected to take a single configuration parameter
53
+ By default, the `Runnable` is expected to take a single configuration parameter
54
54
  called `session_id` which is a string. This parameter is used to create a new
55
- or look up an existing chat message history that matches the given session_id.
55
+ or look up an existing chat message history that matches the given `session_id`.
56
56
 
57
57
  In this case, the invocation would look like this:
58
58
 
@@ -117,12 +117,12 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
117
117
 
118
118
  ```
119
119
 
120
- Example where the wrapped Runnable takes a dictionary input:
120
+ Example where the wrapped `Runnable` takes a dictionary input:
121
121
 
122
122
  ```python
123
123
  from typing import Optional
124
124
 
125
- from langchain_community.chat_models import ChatAnthropic
125
+ from langchain_anthropic import ChatAnthropic
126
126
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
127
127
  from langchain_core.runnables.history import RunnableWithMessageHistory
128
128
 
@@ -166,7 +166,7 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
166
166
  print(store) # noqa: T201
167
167
  ```
168
168
 
169
- Example where the session factory takes two keys, user_id and conversation id):
169
+ Example where the session factory takes two keys (`user_id` and `conversation_id`):
170
170
 
171
171
  ```python
172
172
  store = {}
@@ -223,21 +223,28 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
223
223
  """
224
224
 
225
225
  get_session_history: GetSessionHistoryCallable
226
- """Function that returns a new BaseChatMessageHistory.
226
+ """Function that returns a new `BaseChatMessageHistory`.
227
+
227
228
  This function should either take a single positional argument `session_id` of type
228
- string and return a corresponding chat message history instance"""
229
+ string and return a corresponding chat message history instance
230
+ """
229
231
  input_messages_key: str | None = None
230
- """Must be specified if the base runnable accepts a dict as input.
231
- The key in the input dict that contains the messages."""
232
+ """Must be specified if the base `Runnable` accepts a `dict` as input.
233
+ The key in the input `dict` that contains the messages.
234
+ """
232
235
  output_messages_key: str | None = None
233
- """Must be specified if the base Runnable returns a dict as output.
234
- The key in the output dict that contains the messages."""
236
+ """Must be specified if the base `Runnable` returns a `dict` as output.
237
+ The key in the output `dict` that contains the messages.
238
+ """
235
239
  history_messages_key: str | None = None
236
- """Must be specified if the base runnable accepts a dict as input and expects a
237
- separate key for historical messages."""
240
+ """Must be specified if the base `Runnable` accepts a `dict` as input and expects a
241
+ separate key for historical messages.
242
+ """
238
243
  history_factory_config: Sequence[ConfigurableFieldSpec]
239
244
  """Configure fields that should be passed to the chat history factory.
240
- See `ConfigurableFieldSpec` for more details."""
245
+
246
+ See `ConfigurableFieldSpec` for more details.
247
+ """
241
248
 
242
249
  def __init__(
243
250
  self,
@@ -254,15 +261,16 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
254
261
  history_factory_config: Sequence[ConfigurableFieldSpec] | None = None,
255
262
  **kwargs: Any,
256
263
  ) -> None:
257
- """Initialize RunnableWithMessageHistory.
264
+ """Initialize `RunnableWithMessageHistory`.
258
265
 
259
266
  Args:
260
- runnable: The base Runnable to be wrapped.
267
+ runnable: The base `Runnable` to be wrapped.
268
+
261
269
  Must take as input one of:
262
270
 
263
271
  1. A list of `BaseMessage`
264
- 2. A dict with one key for all messages
265
- 3. A dict with one key for the current input string/message(s) and
272
+ 2. A `dict` with one key for all messages
273
+ 3. A `dict` with one key for the current input string/message(s) and
266
274
  a separate key for historical messages. If the input key points
267
275
  to a string, it will be treated as a `HumanMessage` in history.
268
276
 
@@ -270,13 +278,15 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
270
278
 
271
279
  1. A string which can be treated as an `AIMessage`
272
280
  2. A `BaseMessage` or sequence of `BaseMessage`
273
- 3. A dict with a key for a `BaseMessage` or sequence of
281
+ 3. A `dict` with a key for a `BaseMessage` or sequence of
274
282
  `BaseMessage`
275
283
 
276
- get_session_history: Function that returns a new BaseChatMessageHistory.
284
+ get_session_history: Function that returns a new `BaseChatMessageHistory`.
285
+
277
286
  This function should either take a single positional argument
278
287
  `session_id` of type string and return a corresponding
279
288
  chat message history instance.
289
+
280
290
  ```python
281
291
  def get_session_history(
282
292
  session_id: str, *, user_id: str | None = None
@@ -295,16 +305,17 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
295
305
  ) -> BaseChatMessageHistory: ...
296
306
  ```
297
307
 
298
- input_messages_key: Must be specified if the base runnable accepts a dict
308
+ input_messages_key: Must be specified if the base runnable accepts a `dict`
299
309
  as input.
300
- output_messages_key: Must be specified if the base runnable returns a dict
310
+ output_messages_key: Must be specified if the base runnable returns a `dict`
301
311
  as output.
302
- history_messages_key: Must be specified if the base runnable accepts a dict
303
- as input and expects a separate key for historical messages.
312
+ history_messages_key: Must be specified if the base runnable accepts a
313
+ `dict` as input and expects a separate key for historical messages.
304
314
  history_factory_config: Configure fields that should be passed to the
305
315
  chat history factory. See `ConfigurableFieldSpec` for more details.
306
- Specifying these allows you to pass multiple config keys
307
- into the get_session_history factory.
316
+
317
+ Specifying these allows you to pass multiple config keys into the
318
+ `get_session_history` factory.
308
319
  **kwargs: Arbitrary additional kwargs to pass to parent class
309
320
  `RunnableBindingBase` init.
310
321
 
@@ -364,7 +375,7 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
364
375
  @property
365
376
  @override
366
377
  def config_specs(self) -> list[ConfigurableFieldSpec]:
367
- """Get the configuration specs for the RunnableWithMessageHistory."""
378
+ """Get the configuration specs for the `RunnableWithMessageHistory`."""
368
379
  return get_unique_config_specs(
369
380
  super().config_specs + list(self.history_factory_config)
370
381
  )
@@ -606,6 +617,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
606
617
 
607
618
 
608
619
  def _get_parameter_names(callable_: GetSessionHistoryCallable) -> list[str]:
609
- """Get the parameter names of the callable."""
620
+ """Get the parameter names of the `Callable`."""
610
621
  sig = inspect.signature(callable_)
611
622
  return list(sig.parameters.keys())
@@ -51,10 +51,10 @@ def identity(x: Other) -> Other:
51
51
  """Identity function.
52
52
 
53
53
  Args:
54
- x: input.
54
+ x: Input.
55
55
 
56
56
  Returns:
57
- output.
57
+ Output.
58
58
  """
59
59
  return x
60
60
 
@@ -63,10 +63,10 @@ async def aidentity(x: Other) -> Other:
63
63
  """Async identity function.
64
64
 
65
65
  Args:
66
- x: input.
66
+ x: Input.
67
67
 
68
68
  Returns:
69
- output.
69
+ Output.
70
70
  """
71
71
  return x
72
72
 
@@ -74,11 +74,11 @@ async def aidentity(x: Other) -> Other:
74
74
  class RunnablePassthrough(RunnableSerializable[Other, Other]):
75
75
  """Runnable to passthrough inputs unchanged or with additional keys.
76
76
 
77
- This Runnable behaves almost like the identity function, except that it
77
+ This `Runnable` behaves almost like the identity function, except that it
78
78
  can be configured to add additional keys to the output, if the input is a
79
79
  dict.
80
80
 
81
- The examples below demonstrate this Runnable works using a few simple
81
+ The examples below demonstrate this `Runnable` works using a few simple
82
82
  chains. The chains rely on simple lambdas to make the examples easy to execute
83
83
  and experiment with.
84
84
 
@@ -164,7 +164,7 @@ class RunnablePassthrough(RunnableSerializable[Other, Other]):
164
164
  input_type: type[Other] | None = None,
165
165
  **kwargs: Any,
166
166
  ) -> None:
167
- """Create e RunnablePassthrough.
167
+ """Create a `RunnablePassthrough`.
168
168
 
169
169
  Args:
170
170
  func: Function to be called with the input.
@@ -180,7 +180,7 @@ class RunnablePassthrough(RunnableSerializable[Other, Other]):
180
180
  @classmethod
181
181
  @override
182
182
  def is_lc_serializable(cls) -> bool:
183
- """Return True as this class is serializable."""
183
+ """Return `True` as this class is serializable."""
184
184
  return True
185
185
 
186
186
  @classmethod
@@ -213,11 +213,11 @@ class RunnablePassthrough(RunnableSerializable[Other, Other]):
213
213
  """Merge the Dict input with the output produced by the mapping argument.
214
214
 
215
215
  Args:
216
- **kwargs: Runnable, Callable or a Mapping from keys to Runnables
217
- or Callables.
216
+ **kwargs: `Runnable`, `Callable` or a `Mapping` from keys to `Runnable`
217
+ objects or `Callable`s.
218
218
 
219
219
  Returns:
220
- A Runnable that merges the Dict input with the output produced by the
220
+ A `Runnable` that merges the `dict` input with the output produced by the
221
221
  mapping argument.
222
222
  """
223
223
  return RunnableAssign(RunnableParallel[dict[str, Any]](kwargs))
@@ -350,7 +350,7 @@ _graph_passthrough: RunnablePassthrough = RunnablePassthrough()
350
350
 
351
351
 
352
352
  class RunnableAssign(RunnableSerializable[dict[str, Any], dict[str, Any]]):
353
- """Runnable that assigns key-value pairs to dict[str, Any] inputs.
353
+ """Runnable that assigns key-value pairs to `dict[str, Any]` inputs.
354
354
 
355
355
  The `RunnableAssign` class takes input dictionaries and, through a
356
356
  `RunnableParallel` instance, applies transformations, then combines
@@ -392,7 +392,7 @@ class RunnableAssign(RunnableSerializable[dict[str, Any], dict[str, Any]]):
392
392
  mapper: RunnableParallel
393
393
 
394
394
  def __init__(self, mapper: RunnableParallel[dict[str, Any]], **kwargs: Any) -> None:
395
- """Create a RunnableAssign.
395
+ """Create a `RunnableAssign`.
396
396
 
397
397
  Args:
398
398
  mapper: A `RunnableParallel` instance that will be used to transform the
@@ -403,7 +403,7 @@ class RunnableAssign(RunnableSerializable[dict[str, Any], dict[str, Any]]):
403
403
  @classmethod
404
404
  @override
405
405
  def is_lc_serializable(cls) -> bool:
406
- """Return True as this class is serializable."""
406
+ """Return `True` as this class is serializable."""
407
407
  return True
408
408
 
409
409
  @classmethod
@@ -668,13 +668,19 @@ class RunnableAssign(RunnableSerializable[dict[str, Any], dict[str, Any]]):
668
668
  yield chunk
669
669
 
670
670
 
671
- class RunnablePick(RunnableSerializable[dict[str, Any], dict[str, Any]]):
672
- """Runnable that picks keys from dict[str, Any] inputs.
671
+ class RunnablePick(RunnableSerializable[dict[str, Any], Any]):
672
+ """`Runnable` that picks keys from `dict[str, Any]` inputs.
673
673
 
674
- RunnablePick class represents a Runnable that selectively picks keys from a
674
+ `RunnablePick` class represents a `Runnable` that selectively picks keys from a
675
675
  dictionary input. It allows you to specify one or more keys to extract
676
- from the input dictionary. It returns a new dictionary containing only
677
- the selected keys.
676
+ from the input dictionary.
677
+
678
+ !!! note "Return Type Behavior"
679
+ The return type depends on the `keys` parameter:
680
+
681
+ - When `keys` is a `str`: Returns the single value associated with that key
682
+ - When `keys` is a `list`: Returns a dictionary containing only the selected
683
+ keys
678
684
 
679
685
  Example:
680
686
  ```python
@@ -687,18 +693,22 @@ class RunnablePick(RunnableSerializable[dict[str, Any], dict[str, Any]]):
687
693
  "country": "USA",
688
694
  }
689
695
 
690
- runnable = RunnablePick(keys=["name", "age"])
691
-
692
- output_data = runnable.invoke(input_data)
696
+ # Single key - returns the value directly
697
+ runnable_single = RunnablePick(keys="name")
698
+ result_single = runnable_single.invoke(input_data)
699
+ print(result_single) # Output: "John"
693
700
 
694
- print(output_data) # Output: {'name': 'John', 'age': 30}
701
+ # Multiple keys - returns a dictionary
702
+ runnable_multiple = RunnablePick(keys=["name", "age"])
703
+ result_multiple = runnable_multiple.invoke(input_data)
704
+ print(result_multiple) # Output: {'name': 'John', 'age': 30}
695
705
  ```
696
706
  """
697
707
 
698
708
  keys: str | list[str]
699
709
 
700
710
  def __init__(self, keys: str | list[str], **kwargs: Any) -> None:
701
- """Create a RunnablePick.
711
+ """Create a `RunnablePick`.
702
712
 
703
713
  Args:
704
714
  keys: A single key or a list of keys to pick from the input dictionary.
@@ -708,7 +718,7 @@ class RunnablePick(RunnableSerializable[dict[str, Any], dict[str, Any]]):
708
718
  @classmethod
709
719
  @override
710
720
  def is_lc_serializable(cls) -> bool:
711
- """Return True as this class is serializable."""
721
+ """Return `True` as this class is serializable."""
712
722
  return True
713
723
 
714
724
  @classmethod
@@ -40,11 +40,11 @@ class RouterInput(TypedDict):
40
40
  key: str
41
41
  """The key to route on."""
42
42
  input: Any
43
- """The input to pass to the selected Runnable."""
43
+ """The input to pass to the selected `Runnable`."""
44
44
 
45
45
 
46
46
  class RouterRunnable(RunnableSerializable[RouterInput, Output]):
47
- """Runnable that routes to a set of Runnables based on Input['key'].
47
+ """`Runnable` that routes to a set of `Runnable` based on `Input['key']`.
48
48
 
49
49
  Returns the output of the selected Runnable.
50
50
 
@@ -74,10 +74,10 @@ class RouterRunnable(RunnableSerializable[RouterInput, Output]):
74
74
  self,
75
75
  runnables: Mapping[str, Runnable[Any, Output] | Callable[[Any], Output]],
76
76
  ) -> None:
77
- """Create a RouterRunnable.
77
+ """Create a `RouterRunnable`.
78
78
 
79
79
  Args:
80
- runnables: A mapping of keys to Runnables.
80
+ runnables: A mapping of keys to `Runnable` objects.
81
81
  """
82
82
  super().__init__(
83
83
  runnables={key: coerce_to_runnable(r) for key, r in runnables.items()}
@@ -90,7 +90,7 @@ class RouterRunnable(RunnableSerializable[RouterInput, Output]):
90
90
  @classmethod
91
91
  @override
92
92
  def is_lc_serializable(cls) -> bool:
93
- """Return True as this class is serializable."""
93
+ """Return `True` as this class is serializable."""
94
94
  return True
95
95
 
96
96
  @classmethod
@@ -28,7 +28,7 @@ class EventData(TypedDict, total=False):
28
28
 
29
29
  This field is only available if the `Runnable` raised an exception.
30
30
 
31
- !!! version-added "Added in version 1.0.0"
31
+ !!! version-added "Added in `langchain-core` 1.0.0"
32
32
  """
33
33
  output: Any
34
34
  """The output of the `Runnable` that generated the event.
@@ -7,8 +7,7 @@ import asyncio
7
7
  import inspect
8
8
  import sys
9
9
  import textwrap
10
- from collections.abc import Callable, Mapping, Sequence
11
- from contextvars import Context
10
+ from collections.abc import Mapping, Sequence
12
11
  from functools import lru_cache
13
12
  from inspect import signature
14
13
  from itertools import groupby
@@ -31,9 +30,11 @@ if TYPE_CHECKING:
31
30
  AsyncIterable,
32
31
  AsyncIterator,
33
32
  Awaitable,
33
+ Callable,
34
34
  Coroutine,
35
35
  Iterable,
36
36
  )
37
+ from contextvars import Context
37
38
 
38
39
  from langchain_core.runnables.schema import StreamEvent
39
40
 
@@ -125,9 +125,11 @@ def print_sys_info(*, additional_pkgs: Sequence[str] = ()) -> None:
125
125
  for dep in sub_dependencies:
126
126
  try:
127
127
  dep_version = metadata.version(dep)
128
- print(f"> {dep}: {dep_version}")
129
128
  except Exception:
130
- print(f"> {dep}: Installed. No version info available.")
129
+ dep_version = None
130
+
131
+ if dep_version is not None:
132
+ print(f"> {dep}: {dep_version}")
131
133
 
132
134
 
133
135
  if __name__ == "__main__":