Skip to content

feat(adapters/langgraph): compile ManagerWorkers into a hierarchical graph - #30

Open
spichen wants to merge 17 commits into
mainfrom
feat/managerworkers-adapter
Open

feat(adapters/langgraph): compile ManagerWorkers into a hierarchical graph#30
spichen wants to merge 17 commits into
mainfrom
feat/managerworkers-adapter

Conversation

@spichen

@spichen spichen commented Jul 25, 2026

Copy link
Copy Markdown
Owner

ManagerWorkers had no LangGraph support: converting one fell through _convert and raised. This compiles it into a hierarchical graph.

                ┌─ delegate_to_w1 ─→ worker_1 ─┐
START → manager ┤                              ├→ manager (loop)
                └─ delegate_to_w2 ─→ worker_2 ─┘
                        │
                        └─ no tool_call ─→ END

The manager becomes a react agent holding one synthetic delegate_to_<worker> tool per worker. The parent graph intercepts that tool call in a conditional edge and fans out one Send per delegation, so a single manager turn can delegate to several workers at once. Each worker runs in an isolated message context and replies with a ToolMessage matched to its tool_call_id. Every call gets answered independently, since an unanswered one leaves a dangling call in the manager's next turn.

Workers are converted recursively into their own CompiledStateGraph and added as subgraph nodes, so astream_events still shows the parent/child boundary (subgraph=True) for tracing and SSE streaming. Nested ManagerWorkers compose through convert with no extra work.

ManagerWorkers now infers its inputs and outputs from the group manager, so it can sit inside a flow: an AgentNode wrapping one exposes input ports, and a DataFlowEdge into it resolves. The graph runs over MessagesState, which can't carry structured inputs inward, so node inputs are rendered into the group-manager prompt and the satisfied ports are dropped. AgentNodeExecutor compiles and caches that graph by rendered prompt, the same key it already uses for plain react agents.

The delegation protocol is deliberately visible: delegate_to_<worker> calls and their lifecycle events stream like any other tool call, because "the manager handed task X to worker Y" is usually the most informative thing a run produces. Nothing wraps astream_events to scrub it. A consumer that would rather not render the routing plumbing can recognize it with is_delegation_tool_name.

Structure

Helpers live in a new _managerworkers.py rather than growing _langgraphconverter.py; _manager_workers_convert_to_langgraph stays on the converter next to its siblings, and the flow-step compile sits on AgentNodeExecutor next to its react-agent equivalent.

Wrapping stream/astream in an execution span was already duplicated between the Agent and Flow paths, and ManagerWorkers needed a third copy. That wrapper is now a single patch_with_execution_span in _execution_span.py, parameterized by span and event factories, and all three paths call it.

Swarm is untouched.

Testing

tests/adapters/langgraph, tests/serialization and tests/validation with SKIP_LLM_TESTS=1, in a venv pinned to constraints/constraints.txt. The only failures are the pre-existing ones that need a live model endpoint. ruff is clean on the changed files.

The 25 ManagerWorkers tests all run offline, including the five flow-step ones in flows/test_managerworkers_node.py. They opt out of the blanket SKIP_LLM_TESTS construction guard via a new allow_llm_config_construction fixture — that guard skips a test the moment it builds an LLM config, which is right for tests that go on to call a model but hides tests that only need a config object and stub the conversion.

🤖 Generated with Claude Code

@spichen
spichen force-pushed the feat/agent-single-string-output branch from cdbe088 to c368d95 Compare July 25, 2026 11:31
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from 3747193 to dba45e2 Compare July 25, 2026 11:31
@spichen
spichen force-pushed the feat/agent-single-string-output branch from c368d95 to 711abb5 Compare July 25, 2026 11:37
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from dba45e2 to 34ad0ab Compare July 25, 2026 11:37
@spichen
spichen force-pushed the feat/agent-single-string-output branch from 711abb5 to bd33dfd Compare July 25, 2026 12:00
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from 34ad0ab to 023ef14 Compare July 25, 2026 12:00
@spichen
spichen force-pushed the feat/agent-single-string-output branch from bd33dfd to 4460030 Compare July 25, 2026 15:30
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from 023ef14 to fa78d18 Compare July 25, 2026 15:30
@spichen
spichen force-pushed the feat/agent-single-string-output branch from 4460030 to 47a3d39 Compare July 25, 2026 16:16
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from fa78d18 to 1aa5a54 Compare July 25, 2026 16:16
@spichen
spichen force-pushed the feat/agent-single-string-output branch from 47a3d39 to cd19935 Compare July 25, 2026 16:48
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from 1aa5a54 to ebf57be Compare July 25, 2026 16:48
@spichen
spichen force-pushed the feat/agent-single-string-output branch from cd19935 to 4373852 Compare July 25, 2026 16:55
@spichen
spichen force-pushed the feat/managerworkers-adapter branch 2 times, most recently from e1a293b to c3ed319 Compare July 25, 2026 18:33
@spichen
spichen changed the base branch from feat/agent-single-string-output to main July 25, 2026 18:33
@spichen
spichen force-pushed the feat/managerworkers-adapter branch from 9fa2184 to 08f689a Compare July 25, 2026 20:52
cesarebernardis and others added 2 commits August 10, 2026 19:24
* Upgrading agent-framework adapter to work on latest release

* Fix api key in langgraph adapter

* Upgrade openai agents

* Generalization

* Remove negative test for api key in langgraph

* Fix autogen

* Fix documentation

* Changelog, fix docs
* feat: add DBMS vector chain LLM config

Signed-off-by: Tanmay Bagaria <tanmay.bagaria@oracle.com>

* Add DBMS Vector Chain LLM configuration

Signed-off-by: Tanmay Bagaria <tanmay.bagaria@oracle.com>

* Address DBMS Vector Chain review feedback

Signed-off-by: Tanmay Bagaria <tanmay.bagaria@oracle.com>

* Clarify DBMS Vector Chain configuration semantics

Signed-off-by: Tanmay Bagaria <tanmay.bagaria@oracle.com>

---------

Signed-off-by: Tanmay Bagaria <tanmay.bagaria@oracle.com>
@cesarebernardis
cesarebernardis force-pushed the feat/managerworkers-adapter branch from ec7909b to 6cdd9fc Compare August 24, 2026 10:07
paul-cayet and others added 8 commits August 25, 2026 11:44
* bump versions

* bump version 26.3.0

* Remove CrewAI adapter

* open new dev cycle

* Revert "Remove CrewAI adapter"

This reverts commit 5ca9197.

---------

Co-authored-by: Son Le <tran.minh.son.le@oracle.com>
Co-authored-by: Damien Hilloulin <damien.hilloulin@oracle.com>
…nagerWorkers

Quality pass over the ManagerWorkers adapter. No behaviour change intended.

Wrapping stream/astream in an execution span was already duplicated between the
Agent and Flow paths, and ManagerWorkers added a third near-verbatim copy. Extract
one `patch_with_execution_span` into `_execution_span.py`, parameterized by span and
event factories, and route all three through it. The `except NotImplementedError`
fallback ladder for the async span protocol was written 12 times; it is now one
helper.

Move the flow-step compile onto `AgentNodeExecutor` as
`_create_manager_workers_with_given_input_values`, next to its react-agent twin.
It was living in `_managerworkers.py` while importing the converter and taking the
executor's private cache as a parameter.

Replace the six per-worker closures in `_wrap_worker_for_subgraph` with module-level
helpers and a `_WorkerSubgraphNode` holding only the graph, so a long-lived node no
longer pins the whole factory frame. Memoize `_make_worker_delegation_tool`, which
depends only on the node name yet re-ran the `@tool` decorator on every compile.

In the converter, collapse the parallel worker list and dict into one list of pairs,
hoist the repeated conversion kwargs, and use the module's existing lazy
`langgraph_graph` instead of a local `MessagesState` import.

Drop `is_delegation_event`: no callers, not exported, unreachable through any
supported import path. `is_delegation_tool_name` stays.

Tests: collapse six copies of the loader and double-patch stack into one
`_load_with_fake_llms` helper, and drop assertions in the roster test that compared
a locally computed string against itself.

Add an `allow_llm_config_construction` fixture so tests that only build an LLM
config and stub the conversion are not skipped by the blanket SKIP_LLM_TESTS guard.
This un-skips the three flow-step tests that the guard was hiding.
…h codebase style

Trim reviewer-directed comments to codebase density; drop the lru_cache on the
delegation-tool factory, the _WorkerSubgraphNode class (now closures) and the
dict-or-object tool-call shim. Rewrite the tests to repo conventions: module-level
pyagentspec imports, a shared _agent() helper, pytestmark instead of an empty
autouse fixture, no section dividers, and behavior-level coverage instead of
micro-tests of private helpers.

Also fix a real bug the flow-step test exposed: the ManagerWorkers parent graph
runs over MessagesState, so a structured_response can never reach the node
executor and any declared output raised ValueError. A ManagerWorkers flow step
now answers its single string output with the manager's final message, and
rejects other output shapes with NotImplementedError.
- Remove the SKIP_LLM_TESTS opt-out fixture from tests/adapters/conftest.py
  and its usages; the tests now skip like every other LLM-config test.
- Rename the delegation tool prefix to __delegate_to__ so it cannot collide
  with a real tool name, and route a delegation only when its suffix is an
  actual worker node.
- Move flows/test_managerworkers_node.py into the main test_managerworkers.py
  since ManagerWorkers is not a Node.
- Enforce that the I/Os of a ManagerWorkers match the I/Os of its group
  manager (same name and type), per the language spec decision.
- Reject unsupported ManagerWorkers flow-step output shapes when the flow is
  converted instead of when the step runs.
- Move the ManagerWorkers flow-step behavior out of AgentNodeExecutor into a
  dedicated ManagerWorkersNodeExecutor, selected once at conversion time in
  _agent_node_convert_to_langgraph. This removes the scattered isinstance
  branches and brings _node_execution.py back under 1000 lines.
- Document why the executor calls the private converter entry point: the
  public convert() caches by component id, which would collapse
  differently-rendered prompt copies into one graph.
- Re-export DELEGATE_TOOL_PREFIX and is_delegation_tool_name from
  pyagentspec.adapters.langgraph so the delegation protocol is actually
  public API.
- Tidy: hoist the stdlib Annotated import, annotate _async_or_sync -> None,
  correct the _MANAGER_NODE_KEY collision-safety comment.
@cesarebernardis
cesarebernardis force-pushed the feat/managerworkers-adapter branch from 0a52773 to bf30c66 Compare August 25, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants