Skip to content

Per-Task Model Override in delegate_task #34489

@pattou76

Description

@pattou76

Problem or Use Case

Problem: The current delegate_task uses delegation.model as a single global config for all subagents. You cannot run one subagent on OpenAI GPT-4o and another on Anthropic Claude Sonnet 4.6 simultaneously in the same session — the model field inside each task is ignored.

Proposed Solution

patou:
J'ai toutes les infos pour rédiger la solution technique. Voici le texte ready-to-paste :


Proposed Solution: Per-Task model Override

Files to modify

tools/delegate_tool.py — 2 targeted changes


Change 1 — Loop around line 2061

Current (line 2061-2083):
for i, t in enumerate(task_list):
task_acp_args = t.get("acp_args") if "acp_args" in t else None
effective_role = _normalize_role(t.get("role") or top_role)
child = _build_child_agent(
task_index=i,
goal=t["goal"],
context=t.get("context"),
toolsets=t.get("toolsets") or toolsets,
model=creds["model"], # ← all tasks get same model
max_iterations=effective_max_iter,
task_count=n_tasks,
parent_agent=parent_agent,
override_provider=creds["provider"],
override_base_url=creds["base_url"],
override_api_key=creds["api_key"],
override_api_mode=creds["api_mode"],
override_acp_command=...,
override_acp_args=...,
role=effective_role,
)

Proposed (add per-task model resolution):
for i, t in enumerate(task_list):
task_acp_args = t.get("acp_args") if "acp_args" in t else None
effective_role = _normalize_role(t.get("role") or top_role)

# ── Per-task model override ──────────────────────────────────────
# Per-task model object: {"provider": "openai", "model": "gpt-4o"}
# wins over the global delegation config.
task_model_obj = t.get("model")
if isinstance(task_model_obj, dict):
    # Extract from {"provider": ..., "model": ...} dict (batch mode)
    per_task_model     = task_model_obj.get("model") or creds["model"]
    per_task_provider  = task_model_obj.get("provider") or creds["provider"]
    per_task_base_url = task_model_obj.get("base_url") or creds["base_url"]
    per_task_api_key   = task_model_obj.get("api_key") or creds["api_key"]
    per_task_api_mode  = task_model_obj.get("api_mode") or creds["api_mode"]
elif isinstance(task_model_obj, str) and task_model_obj.strip():
    # Backwards-compatible string shorthand: "openai/gpt-4o"
    per_task_model    = task_model_obj
    per_task_provider = creds["provider"]  # inherit provider from delegation config
    per_task_base_url = creds["base_url"]
    per_task_api_key  = creds["api_key"]
    per_task_api_mode = creds["api_mode"]
else:
    # No override — fall back to delegation config
    per_task_model     = creds["model"]
    per_task_provider = creds["provider"]
    per_task_base_url = creds["base_url"]
    per_task_api_key  = creds["api_key"]
    per_task_api_mode = creds["api_mode"]

child = _build_child_agent(
    task_index=i,
    goal=t["goal"],
    context=t.get("context"),
    toolsets=t.get("toolsets") or toolsets,
    model=per_task_model,          # ← per-task wins now
    max_iterations=effective_max_iter,
    task_count=n_tasks,
    parent_agent=parent_agent,
    override_provider=per_task_provider,
    override_base_url=per_task_base_url,
    override_api_key=per_task_api_key,
    override_api_mode=per_task_api_mode,
    override_acp_command=...,
    override_acp_args=...,
    role=effective_role,
)

Change 2 — Nothing else needed

_build_child_agent already accepts model as a parameter and propagates it to AIAgent. The existing override chain (override_provider, override_base_url, override_api_key) already handles credential routing per-provider.


Backwards Compatibility

delegate_task(goal="...") (single)

• Scenario: delegate_task(goal="...") (single)

• Behavior: Uses creds["model"] (unchanged)

delegate_task(tasks=[{"goal": "...", "model": {"provider": "openai", "model": "gpt-4o"}}])

• Scenario: delegate_task(tasks=[{"goal": "...", "model": {"provider": "openai", "model": "gpt-4o"}}])

• Behavior: Per-task override wins
(1/2)

patou:
delegate_task(tasks=[{"goal": "..."}]) (batch, no model)

• Scenario: delegate_task(tasks=[{"goal": "..."}]) (batch, no model)

• Behavior: Uses creds["model"] (unchanged)

delegate_task(tasks=[{"goal": "...", "model": "openai/gpt-4o"}]) (string shorthand)

• Scenario: delegate_task(tasks=[{"goal": "...", "model": "openai/gpt-4o"}]) (string shorthand)

• Behavior: Parsed as "provider/model" fallback


Testing hint

The existing test suite (tests/tools/test_delegate.py) already mocks child.model. Add a test that passes tasks=[{"goal": "A", "model": {"provider": "openai", "model": "gpt-4o"}}, {"goal": "B", "model": {"provider": "anthropic", "model": "claude-sonnet-4-6"}}] and asserts that two children get different models.


Summary: ~30 lines added, zero breaking changes, enables genuine multi-model parallel subagents. The implementation is already halfway there — _build_child_agent already accepts model as a param, it just needed per-task resolution before passing it. (2/2)

Alternatives Considered

No response

Feature Type

New tool

Scope

None

Contribution

  • I'd like to implement this myself and submit a PR

Debug Report (optional)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/agentCore agent loop, run_agent.py, prompt buildertool/delegateSubagent delegationtype/featureNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions