langgraph 1.2.3__tar.gz → 1.2.4__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.
- {langgraph-1.2.3 → langgraph-1.2.4}/PKG-INFO +1 -1
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/main.py +23 -2
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/transformers.py +17 -12
- {langgraph-1.2.3 → langgraph-1.2.4}/pyproject.toml +1 -1
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_runtime.py +54 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_lifecycle_transformer.py +32 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/uv.lock +1 -1
- {langgraph-1.2.3 → langgraph-1.2.4}/.gitignore +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/LICENSE +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/Makefile +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/README.md +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/__main__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/fanout_to_subgraph.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/pydantic_state.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/react_agent.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/sequential.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/serde_allowlist.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/wide_dict.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/bench/wide_state.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_cache.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_config.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_constants.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_fields.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_future.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_pydantic.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_queue.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_replay.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_retry.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_runnable.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_scratchpad.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_serde.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_timeout.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/_internal/_typing.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/callbacks.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/any_value.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/base.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/binop.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/delta.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/ephemeral_value.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/last_value.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/named_barrier_value.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/topic.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/channels/untracked_value.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/config.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/constants.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/errors.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/func/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/graph/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/graph/_branch.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/graph/_node.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/graph/message.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/graph/state.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/graph/ui.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/managed/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/managed/base.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/managed/is_last_step.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_algo.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_call.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_checkpoint.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_config.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_draw.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_executor.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_io.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_log.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_loop.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_messages.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_read.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_remote_run_stream.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_retry.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_runner.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_tools.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_utils.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_validate.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/_write.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/debug.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/protocol.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/remote.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/pregel/types.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/py.typed +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/runtime.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/_convert.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/_mux.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/_types.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/run_stream.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/stream/stream_channel.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/types.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/typing.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/utils/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/utils/config.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/utils/runnable.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/version.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/langgraph/warnings.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/__init__.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/__snapshots__/test_large_cases.ambr +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/__snapshots__/test_pregel.ambr +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/__snapshots__/test_pregel_async.ambr +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/agents.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/any_int.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/any_str.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/compose-postgres.yml +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/compose-redis.yml +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/conftest.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/conftest_checkpointer.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/conftest_store.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/example_app/example_graph.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/example_app/langgraph.json +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/example_app/requirements.txt +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/fake_chat.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/fake_tracer.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/memory_assert.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/messages.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_algo.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_channels.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_checkpoint_migration.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_config_async.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_delta_channel_benchmark.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_delta_channel_exit_mode.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_delta_channel_id_stability.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_delta_channel_migration.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_delta_channel_supersteps_bound.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_deprecation.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_graph_callbacks.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_interleave_arrival_order.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_interrupt_migration.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_interruption.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_large_cases.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_large_cases_async.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_managed_values.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_messages_state.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_parent_command.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_parent_command_async.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_pregel.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_pregel_async.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_pregel_debug.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_pregel_stream_events_v3.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_pydantic.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_remote_graph.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_remote_graph_v3.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_retry.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_runnable.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_serde_allowlist.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_state.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_before_builtins.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_data_transformers.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_events_v3.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_events_v3_e2e.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_events_v3_kwarg_forwarding.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_messages_transformer.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_stream_subgraph_transformer.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_subgraph_persistence.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_subgraph_persistence_async.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_time_travel.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_time_travel_async.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_tool_stream_handler.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_tracing_interops.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_type_checking.py +0 -0
- {langgraph-1.2.3 → langgraph-1.2.4}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
4
4
|
Summary: Building stateful, multi-actor applications with LLMs
|
|
5
5
|
Project-URL: Homepage, https://docs.langchain.com/oss/python/langgraph/overview
|
|
6
6
|
Project-URL: Documentation, https://reference.langchain.com/python/langgraph/
|
|
@@ -2835,7 +2835,9 @@ class Pregel(
|
|
|
2835
2835
|
config[CONF][CONFIG_KEY_DURABILITY] = durability_
|
|
2836
2836
|
|
|
2837
2837
|
# build server_info from metadata + parent runtime
|
|
2838
|
-
parent_runtime =
|
|
2838
|
+
parent_runtime = _coerce_parent_runtime(
|
|
2839
|
+
config[CONF].get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME)
|
|
2840
|
+
)
|
|
2839
2841
|
server_info = _build_server_info(config, parent_runtime)
|
|
2840
2842
|
|
|
2841
2843
|
runtime = Runtime(
|
|
@@ -3276,7 +3278,9 @@ class Pregel(
|
|
|
3276
3278
|
config[CONF][CONFIG_KEY_DURABILITY] = durability_
|
|
3277
3279
|
|
|
3278
3280
|
# build server_info from metadata + parent runtime
|
|
3279
|
-
parent_runtime =
|
|
3281
|
+
parent_runtime = _coerce_parent_runtime(
|
|
3282
|
+
config[CONF].get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME)
|
|
3283
|
+
)
|
|
3280
3284
|
server_info = _build_server_info(config, parent_runtime)
|
|
3281
3285
|
|
|
3282
3286
|
runtime = Runtime(
|
|
@@ -4253,6 +4257,23 @@ def _resolve_parent_ns(
|
|
|
4253
4257
|
return tuple(ns.split(NS_SEP))
|
|
4254
4258
|
|
|
4255
4259
|
|
|
4260
|
+
def _coerce_parent_runtime(value: Any) -> Runtime[Any]:
|
|
4261
|
+
"""Normalize the value stored under `CONFIG_KEY_RUNTIME` into a `Runtime`.
|
|
4262
|
+
|
|
4263
|
+
During a graph run this is always a `Runtime` the framework created and
|
|
4264
|
+
published for child tasks to inherit. Layers outside the run (for example a
|
|
4265
|
+
server's graph-factory path) may instead seed an object that only carries
|
|
4266
|
+
`context`/`store`. Adopt its `context` so context set at the graph level
|
|
4267
|
+
plumbs through (`merge` lets the run's own `context` take precedence when
|
|
4268
|
+
one is provided). `store` is resolved separately (passed to the graph
|
|
4269
|
+
directly), so it is not read off here. `merge` then combines this with the
|
|
4270
|
+
run's own runtime, including the framework's `control`.
|
|
4271
|
+
"""
|
|
4272
|
+
if isinstance(value, Runtime):
|
|
4273
|
+
return value
|
|
4274
|
+
return Runtime(context=getattr(value, "context", None))
|
|
4275
|
+
|
|
4276
|
+
|
|
4256
4277
|
def _build_server_info(
|
|
4257
4278
|
config: RunnableConfig, parent_runtime: Runtime[Any]
|
|
4258
4279
|
) -> ServerInfo | None:
|
|
@@ -419,6 +419,11 @@ class _TasksLifecycleBase(StreamTransformer):
|
|
|
419
419
|
# `node:<task_id>` shares this task_id, so a subagent recovers the tool
|
|
420
420
|
# call that spawned it (cross-payload).
|
|
421
421
|
self._pending_tool_calls: dict[str, str] = {}
|
|
422
|
+
# `cause` for the current `_on_started` dispatch, set immediately before
|
|
423
|
+
# the call and read by overrides that surface it. Keeping `cause` off the
|
|
424
|
+
# `_on_started` signature means overrides predating it (e.g. deepagents'
|
|
425
|
+
# `SubagentTransformer`) don't break.
|
|
426
|
+
self._pending_cause: LifecycleCause | None = None
|
|
422
427
|
|
|
423
428
|
# --- Template-method hooks (subclass overrides) ---
|
|
424
429
|
|
|
@@ -431,10 +436,11 @@ class _TasksLifecycleBase(StreamTransformer):
|
|
|
431
436
|
ns: tuple[str, ...],
|
|
432
437
|
graph_name: str | None,
|
|
433
438
|
trigger_call_id: str | None,
|
|
434
|
-
*,
|
|
435
|
-
cause: LifecycleCause | None = None,
|
|
436
439
|
) -> None:
|
|
437
|
-
"""Fired once per discovered namespace (first observed task event).
|
|
440
|
+
"""Fired once per discovered namespace (first observed task event).
|
|
441
|
+
|
|
442
|
+
The triggering `cause` (if any) is available as `self._pending_cause`.
|
|
443
|
+
"""
|
|
438
444
|
raise NotImplementedError
|
|
439
445
|
|
|
440
446
|
def _on_terminal(
|
|
@@ -532,7 +538,10 @@ class _TasksLifecycleBase(StreamTransformer):
|
|
|
532
538
|
tool_call_id = self._pending_tool_calls.get(trigger_call_id)
|
|
533
539
|
if tool_call_id:
|
|
534
540
|
cause = {"type": "toolCall", "tool_call_id": str(tool_call_id)}
|
|
535
|
-
|
|
541
|
+
# Deliver `cause` via instance state, not the call signature, so
|
|
542
|
+
# `_on_started` stays backward-compatible with overrides predating it.
|
|
543
|
+
self._pending_cause = cause
|
|
544
|
+
self._on_started(ns, graph_name, trigger_call_id)
|
|
536
545
|
if trigger_call_id is not None:
|
|
537
546
|
self._open[ns] = trigger_call_id
|
|
538
547
|
|
|
@@ -631,8 +640,6 @@ class LifecycleTransformer(_TasksLifecycleBase):
|
|
|
631
640
|
ns: tuple[str, ...],
|
|
632
641
|
graph_name: str | None,
|
|
633
642
|
trigger_call_id: str | None,
|
|
634
|
-
*,
|
|
635
|
-
cause: LifecycleCause | None = None,
|
|
636
643
|
) -> None:
|
|
637
644
|
if trigger_call_id is None:
|
|
638
645
|
# Without a task id we can't correlate a parent-result
|
|
@@ -643,6 +650,7 @@ class LifecycleTransformer(_TasksLifecycleBase):
|
|
|
643
650
|
if graph_name:
|
|
644
651
|
payload["graph_name"] = graph_name
|
|
645
652
|
payload["trigger_call_id"] = trigger_call_id
|
|
653
|
+
cause = self._pending_cause
|
|
646
654
|
if cause is not None:
|
|
647
655
|
payload["cause"] = cause
|
|
648
656
|
self._channel.push(payload)
|
|
@@ -707,8 +715,6 @@ class SubgraphTransformer(_TasksLifecycleBase):
|
|
|
707
715
|
ns: tuple[str, ...],
|
|
708
716
|
graph_name: str | None,
|
|
709
717
|
trigger_call_id: str | None,
|
|
710
|
-
*,
|
|
711
|
-
cause: LifecycleCause | None = None,
|
|
712
718
|
) -> None:
|
|
713
719
|
if self._mux is None:
|
|
714
720
|
return
|
|
@@ -717,10 +723,9 @@ class SubgraphTransformer(_TasksLifecycleBase):
|
|
|
717
723
|
except RuntimeError:
|
|
718
724
|
return
|
|
719
725
|
handle_cls = AsyncSubgraphRunStream if child_mux.is_async else SubgraphRunStream
|
|
720
|
-
# `cause`
|
|
721
|
-
# concern
|
|
722
|
-
#
|
|
723
|
-
# keep the `_on_started` template signature uniform across transformers.
|
|
726
|
+
# The triggering `cause` (on `self._pending_cause`) is a wire/lifecycle
|
|
727
|
+
# concern carried on `LifecyclePayload`; the in-process subgraph
|
|
728
|
+
# navigation handle does not expose it.
|
|
724
729
|
handle = handle_cls(
|
|
725
730
|
mux=child_mux,
|
|
726
731
|
path=ns,
|
|
@@ -1164,3 +1164,57 @@ def test_execution_info_inherited_by_subgraph() -> None:
|
|
|
1164
1164
|
# task_id appears in its own namespace segment
|
|
1165
1165
|
assert main_info.task_id in main_info.checkpoint_ns
|
|
1166
1166
|
assert sub_info.task_id in sub_info.checkpoint_ns
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
def test_foreign_object_in_runtime_slot_is_coerced() -> None:
|
|
1170
|
+
"""A non-`Runtime` under `CONFIG_KEY_RUNTIME` is adopted, not crashed on,
|
|
1171
|
+
and the `context` it carries is plumbed through.
|
|
1172
|
+
|
|
1173
|
+
Layers outside a run (e.g. a server's graph-factory path, like LangGraph
|
|
1174
|
+
API) seed an object carrying `context`/`store` into the runtime slot. The
|
|
1175
|
+
run must still execute (regression for `AttributeError: '...' object has no
|
|
1176
|
+
attribute 'control'`), and `context` set at that level must reach nodes via
|
|
1177
|
+
`merge` when no per-run `context` is provided. `store` is resolved
|
|
1178
|
+
separately, so it is not read off the foreign object in the coercion.
|
|
1179
|
+
"""
|
|
1180
|
+
from langgraph.store.memory import InMemoryStore
|
|
1181
|
+
|
|
1182
|
+
from langgraph._internal._constants import CONFIG_KEY_RUNTIME
|
|
1183
|
+
|
|
1184
|
+
store = InMemoryStore()
|
|
1185
|
+
graph_level_context = {"source": "graph-level"}
|
|
1186
|
+
|
|
1187
|
+
class _ServerLikeRuntime:
|
|
1188
|
+
"""Carries context/store but is not a `Runtime` (no control/merge)."""
|
|
1189
|
+
|
|
1190
|
+
def __init__(self) -> None:
|
|
1191
|
+
self.context = graph_level_context
|
|
1192
|
+
self.store = store
|
|
1193
|
+
|
|
1194
|
+
seen: dict[str, Any] = {}
|
|
1195
|
+
|
|
1196
|
+
class State(TypedDict, total=False):
|
|
1197
|
+
text: str
|
|
1198
|
+
|
|
1199
|
+
def echo(state: State, runtime: Runtime) -> State:
|
|
1200
|
+
seen["context"] = runtime.context
|
|
1201
|
+
seen["store"] = runtime.store
|
|
1202
|
+
return {"text": (state.get("text") or "") + " echoed"}
|
|
1203
|
+
|
|
1204
|
+
builder = StateGraph(State)
|
|
1205
|
+
builder.add_node("echo", echo)
|
|
1206
|
+
builder.add_edge(START, "echo")
|
|
1207
|
+
builder.add_edge("echo", END)
|
|
1208
|
+
graph = builder.compile()
|
|
1209
|
+
|
|
1210
|
+
# No per-run `context=`: the graph-level context on the slot must plumb through.
|
|
1211
|
+
result = graph.invoke(
|
|
1212
|
+
{"text": "hi"},
|
|
1213
|
+
{"configurable": {CONFIG_KEY_RUNTIME: _ServerLikeRuntime()}},
|
|
1214
|
+
)
|
|
1215
|
+
|
|
1216
|
+
assert result == {"text": "hi echoed"}
|
|
1217
|
+
# context set at the graph level is plumbed through to the node via merge
|
|
1218
|
+
assert seen["context"] == graph_level_context
|
|
1219
|
+
# store still reaches the node (resolved separately, not via the coercion)
|
|
1220
|
+
assert seen["store"] is store
|
|
@@ -25,6 +25,7 @@ from langgraph.stream._mux import StreamMux
|
|
|
25
25
|
from langgraph.stream.transformers import (
|
|
26
26
|
LifecyclePayload,
|
|
27
27
|
LifecycleTransformer,
|
|
28
|
+
_TasksLifecycleBase,
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
TS = int(time.time() * 1000)
|
|
@@ -678,3 +679,34 @@ def test_lifecycle_subagent_terminal_roundtrip() -> None:
|
|
|
678
679
|
started, _completed = subagent
|
|
679
680
|
assert started["graph_name"] == "weather_agent"
|
|
680
681
|
assert started["cause"] == {"type": "toolCall", "tool_call_id": "call_w"}
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def test_on_started_override_without_cause_is_backward_compatible() -> None:
|
|
685
|
+
"""An `_on_started` override with the original 3-arg signature must work.
|
|
686
|
+
|
|
687
|
+
`cause` is delivered via `self._pending_cause`, not the call signature, so
|
|
688
|
+
older/third-party subclasses (e.g. deepagents' `SubagentTransformer`) that
|
|
689
|
+
override `_on_started(self, ns, graph_name, trigger_call_id)` keep working —
|
|
690
|
+
no `TypeError: _on_started() got an unexpected keyword argument 'cause'`.
|
|
691
|
+
"""
|
|
692
|
+
seen: list[tuple] = []
|
|
693
|
+
|
|
694
|
+
class _LegacyTransformer(_TasksLifecycleBase):
|
|
695
|
+
def init(self) -> dict[str, Any]:
|
|
696
|
+
return {}
|
|
697
|
+
|
|
698
|
+
def _should_track(self, ns: tuple[str, ...]) -> bool:
|
|
699
|
+
return len(ns) == 1 # direct child of the root scope
|
|
700
|
+
|
|
701
|
+
# Deliberately omits `cause` — mirrors a pre-`cause` override.
|
|
702
|
+
def _on_started(self, ns, graph_name, trigger_call_id) -> None: # type: ignore[override]
|
|
703
|
+
seen.append((ns, graph_name, trigger_call_id))
|
|
704
|
+
|
|
705
|
+
def _on_terminal(self, ns, status, error) -> None:
|
|
706
|
+
pass
|
|
707
|
+
|
|
708
|
+
transformer = _LegacyTransformer(scope=())
|
|
709
|
+
# Must not raise: the base calls `_on_started` without a `cause` kwarg.
|
|
710
|
+
transformer.process(_tasks_start(["agent:abc123"], task_id="abc123", name="agent"))
|
|
711
|
+
|
|
712
|
+
assert seen == [(("agent:abc123",), "agent", "abc123")]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|