Skip to content

Add per-sub-agent provider routing#3969

Closed
heyparth1 wants to merge 2 commits into
Hmbown:mainfrom
heyparth1:brutus/issue-3965
Closed

Add per-sub-agent provider routing#3969
heyparth1 wants to merge 2 commits into
Hmbown:mainfrom
heyparth1:brutus/issue-3965

Conversation

@heyparth1

@heyparth1 heyparth1 commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Status: held for the v0.8.68 fleet lane per maintainer triage — the
branch is currently dirty against main and targets the pre-#4093 spawn path.
Recommended path: rebase and re-route through fleet profile fields (not
close): #4137 covers per-profile provider/model/thinking authoring, but the
#3965 acceptance also needs the resolution side this PR builds — a validated
provider-name → client route for custom (LM Studio-style) endpoints with a
loud failure on unknown providers. Re-landing plan below.

Re-landing plan (post-#4093 fleet surface)

What maps where when this is rebased onto the fleet taxonomy (#4136/#4137):

  • Survives as-is (redesign-agnostic): subagent_client_for_provider_route()
    — provider name (built-in id or [providers.<name>] custom entry) → validated
    client + wire model via the v0.8.67: Ship configured-provider route manager for /provider and /model #3830 route resolver + from_candidate, with the
    explicit unknown-provider rejection. This is the dispatch-time primitive an
    AgentProfile provider field needs regardless of where the field lives;
    FleetProfile.model is documented as "not an auth or endpoint selector", and
    this supplies exactly that missing executable decision. The four regression
    tests and the LM Studio docs example carry over with it.
  • Re-expressed on the fleet surface: the [subagents.routes.<role>] keying
    (role→type→default) becomes a provider field on AgentProfile authoring
    alongside the existing model / model_class_hint / route_tier, resolved in
    the fleet worker-assignment route resolution instead of the legacy
    spawn_subagent_from_input hook.
  • Dropped in the rebase: the pre-fleet spawn-path client swap and the
    SubagentProviderRouting runtime bundle, if the fleet dispatch path already
    carries the config snapshot needed to build routed clients.

If #4137 grows to fully cover explicit provider assignment including custom
endpoints and the graceful-failure acceptance, closing this in its favor (with
the primitive + tests harvested) is equally fine.

Adds explicit per-sub-agent provider routing (#3965): a new
[subagents.routes.<role>] config table pins a sub-agent role or type to a
specific provider — and optionally a model — so one session can, for example,
run explore/format children on a local LM Studio endpoint while general
runs on DeepSeek, with no manual provider switching.

[providers.lm-studio]
kind     = "openai-compatible"
base_url = "http://localhost:1234/v1"
api_key  = "lm-studio"

[subagents.routes.explore]
provider = "lm-studio"
model    = "qwen-2.5-7b"

[subagents.routes.general]
provider = "deepseek"
model    = "deepseek-v4-flash"

How it works:

  • config.rs: new SubagentRouteConfig + routes table on SubagentsConfig,
    exposed via Config::subagent_provider_routes() (lowercased keys, trimmed
    values). Same role/type/default key precedence as [subagents.models].
  • tools/subagent/mod.rs: SubAgentRuntime carries an
    Option<Arc<SubagentProviderRouting>> bundle (routes + config snapshot).
    At spawn, a matching route builds the child's client through the existing
    v0.8.67: Ship configured-provider route manager for /provider and /model #3830 route resolver (route_runtime::resolve_runtime_route +
    DeepSeekClient::from_candidate) — the same path /provider switching
    uses — and swaps it into the child runtime. The routed client drops with
    the child; no new auth/client-lifecycle machinery.
  • core/engine.rs: routing bundle is rebuilt from api_config at each
    runtime construction, so mid-session /provider changes are picked up.
  • Backward compatible: roles without a route inherit the parent's client on
    the unchanged legacy path. A per-call explicit model still wins over the
    route's model; both validate against the routed provider. A route naming a
    provider that is neither built-in nor a [providers.<name>] custom entry
    fails that spawn with a clear error instead of silently falling back to
    DeepSeek.
  • docs/SUBAGENTS.md: new "Per-role provider routes (Per-sub-agent provider assignment (explicit routing) + LM Studio support #3965)" section with the
    LM Studio example above.

Scope boundary

This PR stays single-purpose: explicit provider/model assignment only.
Intentionally out of scope, as follow-ups once the fleet redesign settles the
taxonomy:

Closes #3965

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features
  • cargo test --workspace --all-features

New regression tests (all green; suite: 5727 passed / 0 failed):

  • config::tests::subagent_provider_routes_parse_and_normalize — TOML parsing
    and key/value normalization for [subagents.routes].
  • tools::subagent::tests::role_provider_route_builds_client_on_routed_provider
    — the issue's headline scenario: parent on DeepSeek, explore routed to an
    LM Studio-style custom provider; asserts the routed client binds to
    http://localhost:1234/v1 with the route's model while the parent client is
    untouched.
  • tools::subagent::tests::role_without_provider_route_inherits_parent
    unrouted roles keep the legacy inherit behavior.
  • tools::subagent::tests::provider_route_with_unknown_provider_fails_clearly
    — unknown provider is rejected with an error naming it and pointing at the
    [providers.<name>] config path.

Checklist

  • Updated docs or comments as needed (docs/SUBAGENTS.md section + doc
    comments on all new config/runtime surfaces)
  • Added or updated tests where relevant
  • Verified TUI behavior manually if UI changes (no UI changes — config,
    spawn path, and engine wiring only)
  • Harvested/co-authored credit uses a GitHub numeric noreply address (not
    a harvest — original work for Per-sub-agent provider assignment (explicit routing) + LM Studio support #3965)

@heyparth1 heyparth1 requested a review from Hmbown as a code owner July 3, 2026 12:16
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Thanks @heyparth1 for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

@heyparth1

Copy link
Copy Markdown
Author

Thanks @JayBeest! Yes, this pins any sub-agent to any configured provider/model (not just local vs cloud); smart/keyword routing is a good follow-up but out of scope here.

@heyparth1

Copy link
Copy Markdown
Author

Thanks for the pointer — I've reshaped the PR description to follow the repository's PR template (Summary / Testing / Checklist) and re-verified the contribution shape from CONTRIBUTING.md: single-purpose scope, tests included, docs updated. I also ran the template's exact checks (cargo fmt --all -- --check, workspace clippy, full test suite — 5727 passed / 0 failed) and applied a small rustfmt pass to the new code so fmt is clean.

@Hmbown

Hmbown commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Thanks @heyparth1. Explicit per-sub-agent provider routing is interesting and the reported local verification is appreciated, but I am leaving it out of 0.8.67 because it is currently blocked and touches provider/model routing semantics for sub-agents. I am cutting 0.8.67 soon; please keep this warm for 0.8.68 after a rebase so we can review the routing behavior and docs without rushing it.

Hmbown commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Thanks for this @heyparth1 — it's genuinely well done: [subagents.routes.<role>] with role→type→default precedence, a loud failure on unknown providers (no silent DeepSeek fallback), inherit-preserving behavior for unrouted roles, and solid tests + docs. The LM Studio custom-endpoint example is exactly the kind of local/cloud split this should enable.

Heads up on timing: we're actively reworking the sub-agent fleet/routing system right now (the #3932#3935 cluster — role taxonomy, model classes, per-role routing, wizard-authored profiles). Your PR lands new per-role routing machinery in the same spawn/resolution path that redesign is about to reshape, and I'd rather not have us design per-role provider routing twice and then reconcile a merge conflict.

So I'm going to hold this and land it aligned with the new fleet system in v0.8.68, rather than merge it into the current path now. Moving it to the v0.8.68 milestone and keeping it open — nothing about the approach here is rejected, it's a sequencing call so this routing slots cleanly into the new taxonomy. I'll revisit it (with credit) once the fleet work is out. Appreciate the contribution.


Generated by Claude Code

@Hmbown Hmbown added this to the v0.8.68 milestone Jul 5, 2026 — with Claude
@JayBeest

JayBeest commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Great update, thanks @Hmbown! Delaying to align with the fleet redesign (#3932#3935) is clearly the right move—better to build on a stable foundation than layer new routing on a path that's about to change.

I'm genuinely impressed with how quickly @heyparth1 turned this around. The implementation matches exactly what I had in mind with the original issue, and the feedback on the approach is really encouraging.

On the broader direction: I'd love to help scope or discuss what comes after this foundation. A few areas I'm thinking about:

Keyword-based routing: e.g., agent_open "explore the codebase" automatically routes to an explorer role with a local model

Cost/performance routing: explicit vs. automatic assignment based on task complexity or context window needs

Model class routing (from #3935): grouping models by capability tier and routing accordingly

Naturally this will enable routing around any 'theme' not only cost/performance.

These all build on the explicit routing this PR establishes.

Where's the best place to continue that conversation? I'm guessing just opening new issues as they crystallize (like I did with this one) is the right approach, but let me know if there's a design thread or discussion space I should be watching.

Thanks again to everyone involved—this is shaping up to be a really powerful foundation.


Co-authored by my Chinese Friend (DeepSeek)

@JayBeest

JayBeest commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Since we're delaying to align with the fleet redesign, I want to suggest we broaden the scope of the new SubagentDefinition struct just slightly—so it natively supports both routing AND environmental constraints. This would unify my explicit routing request (#3965) with the constitutional crisis (#4032) in one clean architecture.

Proposed struct additions:

routing_hint (string, optional): Values like 'cheapest', 'fastest', 'most_capable', or explicit 'provider/model'. This resolves against the available fleet at dispatch time.

tool_restrictions (list of strings, optional): Explicitly disabled tools (e.g., ["write_file", "execute_python_script"]).

How they work together to fix the Constitution bug:
For the QA Spec-Analysis skill (where temp scripts keep appearing), we'd define:
toml

[subagents.strict_analyzer]
role = "qa_analyzer"
routing_hint = "cheapest" # or explicitly "lm-studio"
tool_restrictions = ["write_file", "execute_python"] # Physically blocks ad-hoc scripts

The router picks the cheapest local model (solving #3965), while the orchestrator strips the disabled tools from the API schema before the LLM even sees them.

Why this is better than just a constraint: flag in SKILL.md:

The constraint lives in the agent definition, not the prompt. It survives context-window overflow.

It explicitly handles the "lying" behavior from #4032—the LLM can think about writing a temp script all it wants, but the write_file tool simply isn't in its toolbox. No rationalization required.

Cross-linking: This is the architectural fix for #4032. I'll cross-link this comment there so the constitutional crisis thread knows this is coming down the pipeline.


Co-authored by my Chinese Friends (DeepSeek)

@heyparth1

Copy link
Copy Markdown
Author

Sounds good @Hmbown — holding for v0.8.68 to align with the fleet redesign (#3932#3935) makes sense, and I'm happy to rebase onto the new taxonomy once it lands; I've updated the PR description to reflect the new target and will keep it warm. @JayBeest, I'd keep routing_hint/tool_restrictions out of this PR so it stays single-purpose and slots cleanly into the new fleet SubagentDefinition — the explicit provider+model route here is the primitive those can resolve onto, so new issues per idea (cross-linked to #3935/#4032) seems like the right place for that conversation.

@JayBeest

JayBeest commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Agree completely - correction / refinement on my earlier suggestion: After digging deeper into the architecture, I now think tool_restrictions doesn't belong in the routing config (SubagentRouteConfig). It fits much better in the Fleet configuration layer (#3932#3935) as a durable capability boundary, enforced at runtime via the existing Tool Registry / gate chain (filter_tool_catalog_for_gates). I've opened a new issue to scope out the runtime hook point specifically: #4042. Let's keep the routing PR focused purely on provider/model assignment.

@heyparth1

Copy link
Copy Markdown
Author

Thanks @JayBeest — agreed on all counts, and #4042 is the right home for tool restrictions as a fleet-layer capability boundary rather than routing config. I've updated the PR description's scope section to point there, so this PR stays purely provider/model assignment and remains ready to rebase onto the fleet taxonomy for v0.8.68.

@heyparth1

Copy link
Copy Markdown
Author

Quick status note: keeping this warm for v0.8.68 as discussed — the branch still checks out clean locally (fmt, clippy, and the full test suite including the routing regression tests all green). Happy to rebase onto the fleet taxonomy whenever the #3932#3935 work lands; just ping me here.

@Hmbown

Hmbown commented Jul 8, 2026

Copy link
Copy Markdown
Owner

v0.8.68 triage note

This PR is still DIRTY against current main and targets the pre-fleet spawn path.

Fleet already has profile-level model / model_class authoring (AgentProfile / worker assignment route resolution in tools/subagent). Landing per-role [subagents.routes.*] needs a rebase onto the post-#4093 fleet surface and alignment with:

Not merging as-is. Next step: either rebase onto main and re-route through Fleet profile fields, or close in favor of #4137 if that fully covers the use case. Leaving open for the fleet lane.

pull Bot pushed a commit to 1sLand99/CodeWhale that referenced this pull request Jul 9, 2026
Allow AgentProfile/Fleet provider pins to preserve user-named
OpenAI-compatible custom providers (e.g. provider = "lm-studio") and
thread that raw id through interactive agent(profile: ...), Fleet
worker --provider launch, and codewhale exec --provider validation.
Unknown or unconfigured provider ids fail closed instead of silently
inheriting or falling back to DeepSeek. AgentProfile remains the
canonical routing surface; do not introduce [subagents.routes].

Fixes Hmbown#3965

Refs Hmbown#3969 (stale alternate surface held; not merged as-is)
@Hmbown

Hmbown commented Jul 9, 2026

Copy link
Copy Markdown
Owner

v0.8.68 harvest triage (lane-perf)

Scouted against current main. Decision: do not harvest into 0.8.68 yet.

Quality of the contributor work remains high (tests, docs, loud unknown-provider failure). Keeping warm for a post-fleet rebase rather than a force-harvest. Credit @heyparth1.

@heyparth1

Copy link
Copy Markdown
Author

Thanks for the triage @Hmbown — my vote is rebase-and-re-route rather than close: #4137 covers the authoring side, but the resolution primitive here (provider name → validated client/route for custom LM Studio-style endpoints, with the loud unknown-provider failure) is what makes an AgentProfile provider field executable, since FleetProfile.model is deliberately not an endpoint selector. I've put a concrete re-landing plan in the PR description mapping what survives as-is (the resolution primitive, tests, docs), what gets re-expressed as profile fields on the fleet surface, and what gets dropped from the pre-fleet spawn path. Happy to do that rebase in the fleet lane once #4136/#4137 settle — or if it's faster, the primitive and tests are cleanly harvestable into #4137.

Hmbown commented Jul 9, 2026

Copy link
Copy Markdown
Owner

yeah, I think this is smart. i'm working on finishing up the version now and will take it from here if you don't have time. thank you so much!!

@Hmbown

Hmbown commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Superseded by the landed Fleet route path rather than mergeable as-is.

The requested explicit per-sub-agent provider/model behavior is now covered on main by #4198 (profile-pinned provider rebinding for in-process TUI spawns) and #4259 (AgentProfile route contract and precedence), with fail-closed route tests for unavailable providers and custom-provider snapshots. That is the post-Fleet shape this PR was held for, so we should not reintroduce the pre-Fleet [subagents.routes.*] surface or force-merge this conflicting branch.

Thank you @heyparth1 for the high-quality implementation, tests, LM Studio/custom-endpoint repro, and for keeping the design focused while Fleet landed. The provider-resolution primitive and regression coverage materially informed the shipped route work.

@Hmbown

Hmbown commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Closing as superseded by the merged Fleet route implementation (#4198, #4259); no code is being discarded from the requested behavior. Thank you @heyparth1 for the contribution and detailed routing tests.

@Hmbown Hmbown closed this Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working v0.8.68 Targeting v0.8.68

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-sub-agent provider assignment (explicit routing) + LM Studio support

3 participants