Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,17 @@ def evaluate_next(self, q: QProduct, run: RunLedger, *, config: KernelConfig) ->
def _participants_from_spec(self, config: KernelConfig) -> list[str]:
spec = getattr(config, "agent_spec", None) or {}
wf = (spec.get("workflow") or {}) if isinstance(spec, dict) else {}
# The coordinator/entry node isn't always literally named "moderator"
# (e.g. sre-triage's entry node is "sre") — use workflow.entry when
# present so the coordinator never ends up delegating to itself.
coordinator_id = str(wf.get("entry") or "moderator").strip() or "moderator"

# 1) moderator.delegates_to (dynamic topology)
# 1) coordinator.delegates_to (dynamic topology)
nodes = wf.get("nodes") or []
for node in nodes:
if not isinstance(node, dict):
continue
if str(node.get("id") or "").strip() != "moderator":
if str(node.get("id") or "").strip() != coordinator_id:
continue
delegates = node.get("delegates_to") or []
if isinstance(delegates, list):
Expand All @@ -141,27 +145,27 @@ def _participants_from_spec(self, config: KernelConfig) -> list[str]:
if out:
return out

# 3) fallback: workflow.nodes excluding moderator
# 3) fallback: workflow.nodes excluding the coordinator
out: list[str] = []
for node in nodes:
if not isinstance(node, dict):
continue
node_id = str(node.get("id") or node.get("agent") or "").strip()
if not node_id or node_id == "moderator":
if not node_id or node_id == coordinator_id:
continue
out.append(node_id)
if out:
return out

# 4) fallback: agency agents list from MAS spec (exclude moderator/self)
# 4) fallback: agency agents list from MAS spec (exclude coordinator/self)
agency = (spec.get("agency") or {}) if isinstance(spec, dict) else {}
rows = agency.get("agents") or []
out = []
for row in rows:
if not isinstance(row, dict):
continue
aid = str(row.get("id") or row.get("name") or "").strip()
if not aid or aid == "moderator":
if not aid or aid == coordinator_id:
continue
out.append(aid)
return out
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class DeterministicLinearPlugin(_DeterministicBase):
plugin_id = "deterministic_linear@v1"
mode = "linear"

def evaluate_next(self, q: QProduct, run: RunLedger, *, config: KernelConfig) -> list[EgressSymbol]:
def evaluate_next(self, q: QProduct, run: RunLedger, config: KernelConfig) -> list[EgressSymbol]:
st = self._state()
if not st.participants:
return self._no_participants(q)
Expand Down
4 changes: 2 additions & 2 deletions runtime/src/mas/runtime/kernel/ingress_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def commit_engine_io_return(
q.dp = DpState.EVALUATING
apply_control_valid(q)
coord_after_ingress(q)
return evaluate(q, run, config)
return evaluate(q, run, config=config)


def apply_engine_io_return(
Expand Down Expand Up @@ -203,6 +203,6 @@ def apply_engine_io_return(
)
q.inflight_kind = "NONE"
q.dp = DpState.EVALUATING
return evaluate(q, run, config)
return evaluate(q, run, config=config)

return commit_engine_io_return(q, run, event, config=config, evaluate=evaluate)
2 changes: 1 addition & 1 deletion runtime/tests/test_parallel_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def test_schedule_parallel_tools_egress_siblings_share_one_parent() -> None:
text="ok",
)
with runtime_binding(None, op):
apply_engine_io_return(q, run, event, config=config, evaluate=lambda *_: [])
apply_engine_io_return(q, run, event, config=config, evaluate=lambda *_, **__: [])

ends = [
e
Expand Down
Loading