Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
13 changes: 6 additions & 7 deletions orchestrator/agent_model_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,12 @@
# default in ``orchestrator/consensus_wrapper.py::build_consensus_wrapped_command``.
DEFAULT_AGENT_MODEL = "opus"

# Built-in default for the refine and plan phases. The drafting-heavy
# upstream phases (analysis, slice DAG, contract shape) run on the
# highest-capability tier; implement and downstream phases stay on
# DEFAULT_AGENT_MODEL. Both pipeline-level ``agent_models`` and the
# repo-level ``default_agent_model`` still override this (precedence
# unchanged — this only splits the tier-3 built-in by role).
FABLE_DEFAULT_MODEL = "fable"
# Built-in default for the refine and plan phases. Fable has been
# disabled, so the drafting-heavy upstream phases (analysis, slice DAG,
# contract shape) now run on opus alongside implement and downstream
# phases. Both pipeline-level ``agent_models`` and the repo-level
# ``default_agent_model`` still override this (precedence unchanged).
FABLE_DEFAULT_MODEL = "opus"

# Effort level pinned on fable-routed agents (threaded to ``--effort``
# on the ``python3 -m egg_agent`` command). Claude Code's built-in
Expand Down
36 changes: 23 additions & 13 deletions orchestrator/tests/test_agent_model_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

1. ``pipeline_config.agent_models.get(role.value)`` — per-pipeline
2. ``get_default_agent_model(repo)`` — per-repo
3. Built-in default — ``"fable"`` for refine/plan phase roles,
``"opus"`` for everything else
3. Built-in default — ``"opus"`` for all roles. Fable has been
disabled, so ``FABLE_DEFAULT_MODEL`` is now ``"opus"`` and the
refine/plan phase roles unify on opus alongside everything else.

Classifier (model string → upstream):

Expand Down Expand Up @@ -756,9 +757,9 @@ def find_spec(self, fullname, path, target=None):

class TestDefaultPathRegression:
"""With ``agent_models={}`` and no repo default, EVERY role resolves
to an Anthropic-route built-in: ``fable`` for the refine/plan phase
roles, ``opus`` for everything else. ``upstream_model`` stays
``None`` on every default path — the slice-2 no-op invariant.
to an Anthropic-route built-in: ``opus`` for all roles now that fable
has been disabled (refine/plan unify on opus). ``upstream_model``
stays ``None`` on every default path — the slice-2 no-op invariant.
"""

def test_implement_phase_roles_default_to_anthropic_opus(self):
Expand Down Expand Up @@ -786,9 +787,13 @@ def test_implement_phase_roles_default_to_anthropic_opus(self):
f"regression: default config must be Anthropic-only"
)

def test_refine_and_plan_roles_default_to_anthropic_fable(self):
def test_refine_and_plan_roles_default_to_anthropic_opus(self):
"""Refine/plan producers and reviewers pick up the built-in
``fable`` default while staying on the Anthropic upstream."""
``opus`` default while staying on the Anthropic upstream.

Fable has been disabled, so ``FABLE_DEFAULT_MODEL`` is now
``"opus"`` — the refine/plan roles unify on opus alongside the
implement and downstream phases."""
resolve_agent_model = _resolver()
AgentRole = _agent_role()
config = _pipeline_config()
Expand All @@ -804,8 +809,8 @@ def test_refine_and_plan_roles_default_to_anthropic_fable(self):
AgentRole.REVIEWER_PLAN,
):
d = resolve_agent_model(role, config, "any/repo")
assert d.claude_code_alias == "fable", (
f"{role.value} should default to fable, got {d.claude_code_alias!r}"
assert d.claude_code_alias == "opus", (
f"{role.value} should default to opus, got {d.claude_code_alias!r}"
)
assert d.upstream == "anthropic"
assert d.upstream_model is None
Expand Down Expand Up @@ -870,9 +875,14 @@ def test_litellm_models_inherit_default_effort(self):

assert classify_model("qwen3-max").effort is None

def test_refine_plan_default_decision_carries_high_effort(self):
"""The built-in fable default for refine/plan roles flows through
``resolve_agent_model`` with the pinned effort attached."""
def test_refine_plan_default_decision_inherits_default_effort(self):
"""The built-in opus default for refine/plan roles flows through
``resolve_agent_model`` carrying ``effort=None``.

Fable has been disabled, so ``FABLE_DEFAULT_MODEL`` is ``"opus"``;
opus is not in ``_FABLE_ALIASES``, so the pinned ``FABLE_EFFORT``
no longer applies and these roles inherit Claude Code's per-model
default — matching the coder/implement roles."""
resolve_agent_model = _resolver()
AgentRole = _agent_role()
config = _pipeline_config()
Expand All @@ -881,5 +891,5 @@ def test_refine_plan_default_decision_carries_high_effort(self):
refiner = resolve_agent_model(AgentRole.REFINER, config, "any/repo")
coder = resolve_agent_model(AgentRole.CODER, config, "any/repo")

assert refiner.effort == "high"
assert refiner.effort is None
assert coder.effort is None
12 changes: 6 additions & 6 deletions orchestrator/tests/test_concurrent_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,14 +1047,14 @@ def _capture_command(prompt_text, **kwargs):
f"consensus wrapper; got {captured.get('model')!r}"
)

def test_default_config_passes_fable_for_refine_plan_role(self):
"""``agent_models == {}`` → refiner spawn passes ``model="fable"``
def test_default_config_passes_opus_for_refine_plan_role(self):
"""``agent_models == {}`` → refiner spawn passes ``model="opus"``
to ``build_consensus_wrapped_command``.

Symmetric to ``test_default_config_passes_opus_to_consensus_wrapper``
— guards the role→model wiring in ``_spawn_agent`` against a
future change that breaks the fable default for the refine/plan
roles (post-PR #3062 split of tier-3 built-in by role).
future change that breaks the opus default for the refine/plan
roles after fable was disabled.
"""
from concurrent_executor import ConcurrentPhaseExecutor
from egg_orchestrator.types import AgentRole
Expand All @@ -1076,8 +1076,8 @@ def _capture_command(prompt_text, **kwargs):
):
executor._spawn_agent(AgentRole.REFINER, prompt_text="run task")

assert captured.get("model") == "fable", (
f"Default-config refiner spawn MUST pass model='fable' to "
assert captured.get("model") == "opus", (
f"Default-config refiner spawn MUST pass model='opus' to "
f"the consensus wrapper; got {captured.get('model')!r}"
)

Expand Down
Loading