1jehuang / 1jehuang/jcode

Feature request: Capability-based model routing (route to vision model only when image input is present)

Open
#819 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

autonomous: no enhancement triage: needs-decision
Dominant language
Rust
Stars
19.9k
Forks
2.3k
Avg merge
2d 7h
Merged PRs (30d)
30

Description

Summary

Request a capability-based / conditional model routing mechanism: route a task to a vision-capable model only when the task input contains images, and to a text-only model otherwise. This is more granular than the static per-role model assignment proposed in #683, and it directly addresses the production failure mode described in #755.

Problem

We run an autonomous multi-agent CI/CD pipeline on jcode with 4 specialized roles (issue triage, implementation, code review, end-to-end verification). Three of the four roles are purely text-based (code diffs, curl+grep on preview URLs, log analysis) and run best on a cheap text-only model (glm-5.2). The fourth role (triage) needs to read image attachments (mockups, screenshots) that users attach to issues — but only sometimes: many issues are text-only and have no images.

Today we have two bad options:

  1. Run all roles on a vision model — 3x the cost for 3 roles that never touch an image.
  2. Run all roles on a text-only model — triage crashes (#755) or silently fabricates image content when an issue has image attachments.

The per-role model override proposed in #683 ([swarm.roles.<role>].model) gets us partway, but it is static: triage would always use the vision model, even on text-only issues where the text-only model is cheaper and sufficient. What we want is dynamic routing based on task input:

"If this task's input contains image content (attachment, screenshot, browser capture), route to a vision-capable model. Otherwise, route to the text-only model."

Proposed mechanism

A routing policy that inspects the task input (or the node's declared capability requirements) and selects the model accordingly. Possible shapes:

Option A — Input-content-based routing (automatic)

The runtime inspects the message history / task input for ContentBlock::Image (or declared image attachments) and routes to a vision-capable model when present, falling back to the text-only model otherwise. Config:

[routing.vision]
on_image_input = "provider/vision-model"
fallback = "provider/text-only-model"

This pairs naturally with the #755 fix (auto-strip images for text-only models): if a vision model is available and routed to, images are kept; if not, they're stripped and the text-only model handles the text portion.

Option B — Per-node capability declaration (DAG-aware)

In the swarm DAG model (SWARM_TASK_GRAPH.md), each node declares its capability requirements, and the scheduler picks a worker from the pool that satisfies them:

[swarm.nodes.triage_with_images]
requires = ["vision"]
model = "provider/vision-model"

[swarm.nodes.triage_text_only]
requires = []
model = "provider/text-only-model"

The DAG expansion step would branch on "does this issue have image attachments?" and emit the appropriate node. This aligns with the "per-node prompts" direction already hinted in SWARM_TASK_GRAPH.md.

Option C — Provider-pool with capability tags

The provider pool is tagged with capabilities (vision, text-only, tool-use, etc.) and the router picks any provider that satisfies the task's declared requirements:

[[providers]]
name = "glm-text"
model = "glm-5.2"
capabilities = ["text-only"]

[[providers]]
name = "vision-model"
model = "..."
capabilities = ["vision", "text"]

Why this is distinct from #683

#683 proposes static per-role model assignment ("coder → fast model, reviewer → strong model"). That's a role-level decision made once at config time.

This request is for dynamic per-task routing within a single role, based on the task's actual input. The same role (triage) may need a vision model on one issue and a text-only model on the next. Static per-role routing can't express that — it would force the vision model on every triage run, or the text-only model on every triage run, but not "vision when images, text-only otherwise."

Both are valuable; they compose. #683 gives per-role defaults, this gives per-task overrides based on input capabilities.

Use case (concrete)

Our triage role processes incoming issues. An issue may contain:

  • Text only → text-only model is sufficient and cheaper.
  • Text + image attachments (mockup, screenshot, logo) → vision model required to actually see the image instead of fabricating a description.

With capability-based routing, triage runs on glm-5.2 (text-only, cheap) for 80% of issues and auto-escalates to a vision model for the 20% that have images — without us writing per-issue branching logic in the agent prompt. The runtime handles the routing; the agent prompt stays model-agnostic.

Related

  • #683 — per-agent/per-role model selection (static). This issue is the dynamic counterpart.
  • #755 — image blocks break text-only sessions. Capability-based routing would prevent images from ever reaching a text-only model in the first place, making #755's auto-strip a defense-in-depth rather than the primary fix.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading SWARM_TASK_GRAPH.md and the related issues #683 and #755 to understand the existing model-selection and image-handling directions. Determine which routing shape is intended before locating the runtime and configuration entry points. Done means image-bearing tasks reach a vision-capable model while text-only tasks use the fallback, with coverage for both paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
ai, cli
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.