Skip to content

feat(cost): route trivial subtasks to Haiku by default (P5.T5)#408

Merged
OBenner merged 2 commits into
developfrom
claude/p5-cheap-model-routing
Jul 7, 2026
Merged

feat(cost): route trivial subtasks to Haiku by default (P5.T5)#408
OBenner merged 2 commits into
developfrom
claude/p5-cheap-model-routing

Conversation

@OBenner

@OBenner OBenner commented Jul 7, 2026

Copy link
Copy Markdown
Owner

What

Roadmap P5·T5: «Роутинг дешёвой модели на тривиальные подзадачи … Простые подзадачи идут на haiku, записано в статистике».

The complexity router (TaskComplexityRouter) is already fully wired — it's called per-subtask in agents/coder.py (the live build loop) and in the shared session factory (core/providers/factory.py), routing low-complexity subtasks to a cheaper tier. But the low tier defaulted to openai/gpt-4o-mini, which under the default claude-only autonomy (AUTO_CODE_AUTONOMY=claude, allowlist {claude,codex}) is refused and falls back to Sonnet — so the cost saving never fired for the common Claude-only setup.

  • config/model_routing.yaml + DEFAULT_MODEL_ROUTING_CONFIGlow tier is now claude / claude-haiku-4-5-20251001. Haiku stays inside the Claude runtime (no second provider needed) and is far cheaper than the Sonnet fallback claude-only users get today. Override to gpt-4o-mini via MODEL_ROUTER_LOW_PROVIDER/MODEL_ROUTER_LOW_MODEL or the YAML.
  • recorded in stats — the routed model flows route.model → with_provider_model → session model → save_token_stats, so it lands in token_stats.json / cost_report.json (P5·T1/T2). No extra wiring needed.

Validation passes: claude-haiku-4-5-20251001 is in cost_calculator.MODEL_PRICING and the claude→anthropic pricing alias is accepted. Cost ordering high(Sonnet 0.045) > medium(gpt-4o 0.0325) > low(Haiku 0.012) still holds.

Acceptance

A trivial subtask under a claude-only runtime now routes to Haiku (not the Sonnet fallback), proven by test_low_complexity_routes_to_haiku_under_claude_only.

Tests

  • Updated the routing-table + defaults assertions to Haiku; new test_low_complexity_routes_to_haiku_under_claude_only; the two fallback/allowlist tests retargeted to the medium (OpenAI) tier so they still exercise provider fallback.
  • 48 passed (test_task_router.py + test_multi_model_orchestration.py), ruff check + format clean.

Roadmap doc line deferred to avoid a merge conflict with #407's in-flight roadmap edit.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Low-complexity tasks now route to a Claude Haiku model by default (instead of the previous OpenAI default).
    • Added/expanded guidance on how to override the low-tier model via configuration.
  • Bug Fixes

    • Improved Claude-only handling for trivial requests, avoiding compatibility/unavailable-style messaging.
    • Refined fallback routing so “low” fallback uses an appropriate provider-specific cheap model, while keeping medium routing complexity consistent when providers are restricted.
  • Tests

    • Updated and extended task router tests to reflect the new routing defaults and fallback behavior.

The complexity router (TaskComplexityRouter, wired in coder.py + the
session factory) already routes low-complexity subtasks to a cheaper
tier, but the 'low' tier defaulted to openai/gpt-4o-mini — which under
the default claude-only autonomy is refused and falls back to Sonnet, so
the cost saving never fired for the common setup.

- config/model_routing.yaml + DEFAULT_MODEL_ROUTING_CONFIG: low tier now
  claude/claude-haiku-4-5-20251001. Haiku stays inside the Claude runtime
  (works at AUTO_CODE_AUTONOMY=claude, no second provider) and is far
  cheaper than the Sonnet fallback. Override to gpt-4o-mini via
  MODEL_ROUTER_LOW_* env vars or the YAML.
- the routed model flows through with_provider_model → the session model,
  so it's recorded in token_stats.json / cost_report.json (P5.T1/T2).

Tests: updated the routing-table + defaults assertions; new
test_low_complexity_routes_to_haiku_under_claude_only proves a trivial
subtask routes to Haiku with no fallback under a claude-only allowlist;
the two fallback/allowlist tests retargeted to the medium (OpenAI) tier
so they still exercise provider fallback. 48 passed (task_router +
multi_model_orchestration), ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Low-complexity routing now defaults to Claude claude-haiku-4-5-20251001 in both configuration and routing resolution. The task router adds a cheap-model map for low-tier fallback selection, and tests are updated for the new provider/model expectations and fallback behavior.

Changes

Low-complexity routing default change

Layer / File(s) Summary
Routing configuration defaults
apps/backend/config/model_routing.yaml, apps/backend/core/providers/task_router_config.py
The low-tier routing target changes from OpenAI gpt-4o-mini to Claude claude-haiku-4-5-20251001, with added override comments.
Low-tier fallback model selection
apps/backend/core/providers/task_router.py
A provider-to-cheap-model map is added, and low-complexity fallback routing now uses that mapped model instead of the provider’s default model.
Task router test updates
tests/test_task_router.py
Low-tier expectations, Claude-only trivial-input routing, fallback assertions, allowlist setup, and missing-YAML defaults are updated to match the new Claude Haiku behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • OBenner/Auto-Coding#205: Both PRs change task-router routing defaults and related test coverage in the same routing code path.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: trivial subtasks now route to Haiku by default.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/p5-cheap-model-routing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Adversarial review caught a regression: with the low tier now defaulting
to claude, a non-Claude single-provider setup (e.g. AI_ENGINE_PROVIDER=
openai, no Claude creds) would fall back for trivial subtasks to the
provider's DEFAULT model (gpt-5.2, $2.50/$10) instead of a cheap one —
~16x the old gpt-4o-mini and inverting the low < medium cost ordering.

- CHEAP_MODEL_BY_PROVIDER maps each provider to its Haiku-equivalent
  (claude→haiku, openai→gpt-4o-mini, google→gemini-1.5-flash)
- _resolve_available_route_config: for the low tier, the fallback now
  uses the cheap model of the available provider, not its default
- test: openai-only runtime routes a trivial subtask to gpt-4o-mini
  (would be the openai default without the fix)

49 passed, ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Oleg Miagkov <mrobenner@gmail.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@github-actions github-actions Bot added size/M and removed size/S labels Jul 7, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_task_router.py (1)

126-140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for the google cheap-fallback path.

Tests cover claude-only (Haiku direct) and openai-fallback (gpt-4o-mini cheap fallback), but there's no equivalent assertion for a google-only/fallback runtime picking CHEAP_MODEL_BY_PROVIDER["google"]. Given the model ID itself is likely stale (see companion comment on task_router.py), a test would have caught this drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_task_router.py` around lines 126 - 140, Add test coverage in
test_task_router for the google cheap-fallback path by exercising a google-only
or fallback routing case in the same area that asserts
low_route/medium_route/high_route behavior. Verify the route selected by
TaskRouter uses CHEAP_MODEL_BY_PROVIDER["google"] for the cheap model choice,
similar to how the existing assertions cover claude and openai fallback paths.
Reference the existing test setup around low_route, medium_route, high_route,
and provider_config so the new assertion fits the current routing matrix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/backend/core/providers/task_router.py`:
- Around line 24-33: The hardcoded CHEAP_MODEL_BY_PROVIDER map in task_router.py
duplicates the config-driven low-tier model settings and can drift from
model_routing.yaml/task_router_config.py. Update the fallback routing logic in
the task router to derive cheap models from the existing configuration source
used for MODEL_ROUTER_LOW_PROVIDER and MODEL_ROUTER_LOW_MODEL, or add a
config-backed cheap_models section and load it there. Keep the provider fallback
behavior in sync with the config so changes to the low-tier default
automatically apply across all providers.

---

Outside diff comments:
In `@tests/test_task_router.py`:
- Around line 126-140: Add test coverage in test_task_router for the google
cheap-fallback path by exercising a google-only or fallback routing case in the
same area that asserts low_route/medium_route/high_route behavior. Verify the
route selected by TaskRouter uses CHEAP_MODEL_BY_PROVIDER["google"] for the
cheap model choice, similar to how the existing assertions cover claude and
openai fallback paths. Reference the existing test setup around low_route,
medium_route, high_route, and provider_config so the new assertion fits the
current routing matrix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8d53404-0864-4c2b-9091-03a2734c2d26

📥 Commits

Reviewing files that changed from the base of the PR and between d291eb7 and 7ad62d4.

📒 Files selected for processing (2)
  • apps/backend/core/providers/task_router.py
  • tests/test_task_router.py

Comment on lines +24 to +33
# Cheap "Haiku-equivalent" model per provider. Used when the low (trivial)
# tier can't reach its configured provider and must fall back: without this,
# the fallback would use the provider's *default* (expensive) model, silently
# upgrading trivial subtasks and inverting the low < medium cost ordering.
CHEAP_MODEL_BY_PROVIDER: dict[str, str] = {
"claude": "claude-haiku-4-5-20251001",
"openai": "gpt-4o-mini",
"google": "gemini-1.5-flash",
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Hardcoded model map duplicates the config-driven default and can silently drift.

CHEAP_MODEL_BY_PROVIDER is a second, code-level source of truth for "the cheap model per provider," parallel to model_routing.yaml/task_router_config.py which already define the low-tier default (claude/claude-haiku-4-5-20251001) and its overrides (MODEL_ROUTER_LOW_PROVIDER/MODEL_ROUTER_LOW_MODEL). If the config-driven low-tier default is changed later (e.g., via env var or YAML), this hardcoded map won't follow, so a claude → openai fallback could route to a stale gpt-4o-mini while the "primary" low tier has moved on. Consider deriving per-provider cheap models from configuration (e.g., a cheap_models section in model_routing.yaml) rather than a separate Python constant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/backend/core/providers/task_router.py` around lines 24 - 33, The
hardcoded CHEAP_MODEL_BY_PROVIDER map in task_router.py duplicates the
config-driven low-tier model settings and can drift from
model_routing.yaml/task_router_config.py. Update the fallback routing logic in
the task router to derive cheap models from the existing configuration source
used for MODEL_ROUTER_LOW_PROVIDER and MODEL_ROUTER_LOW_MODEL, or add a
config-backed cheap_models section and load it there. Keep the provider fallback
behavior in sync with the config so changes to the low-tier default
automatically apply across all providers.

@OBenner OBenner merged commit ba4367a into develop Jul 7, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant