Skip to content

Feat/smart auto routing - #908

Open
atulya-srivastava wants to merge 3 commits into
Nano-Collective:mainfrom
atulya-srivastava:feat/smart-auto-routing
Open

Feat/smart auto routing#908
atulya-srivastava wants to merge 3 commits into
Nano-Collective:mainfrom
atulya-srivastava:feat/smart-auto-routing

Conversation

@atulya-srivastava

Copy link
Copy Markdown

Implements a Smart Auto-Routing cascading model strategy that dynamically routes user prompts between a lightweight model (for simple/trivial turns) and the primary "strong" model (for complex reasoning & code edits).

Closes #891.

Overview

When enabled:

  • Classifier Engine: Evaluates prompt complexity using heuristic keyword matching, code block counts, and character length thresholds.
  • Slash Command: Provides /smartroute [on|off|simple <model>|threshold <low|medium|high>] to toggle and configure auto-routing at runtime.
  • Per-Turn Model Swapping: Temporarily switches the LLM client to the simple model for trivial turns, with a try...finally block guaranteeing model restoration for subsequent turns.
  • State Persistence: Persists settings to nanocoder-preferences.json under "smartRouting", ensuring choices survive terminal restarts.

Notes for Reviewers

  • Fail-Safe Default: Prompts with no explicit trivial keywords default to the primary strong model to prevent sending complex/ambiguous tasks to underpowered models.
  • Model Restoration Guarantee: Model swapping is wrapped in a try...finally block inside useChatHandler.tsx so that aborts, errors, or cancellations always restore the user's primary model cleanly.

Files

  • source/ai-sdk-client/smart-router.ts — Core complexity classifier engine (classifyTurnComplexity & autoSelectSimpleModel)
  • source/ai-sdk-client/smart-router.spec.ts — 16 unit tests for complexity classification rules, custom options, and model detection
  • source/commands/lazy-registry.ts — Registered /smartroute in slash command lazy loader
  • source/app/utils/handlers/smartroute-handler.ts — Slash command handler for /smartroute subcommands
  • source/app/utils/app-util.ts — Special command dispatch integration
  • source/hooks/useAppState.tsx — Managed smartRouting state with persistence handlers
  • source/hooks/chat-handler/useChatHandler.tsx — Injected per-turn model swapping logic into the chat loop
  • source/hooks/useAppHandlers.tsx & source/types/app.ts — Prop-drilled smartRouting state to message submission handlers
  • source/types/config.ts & source/config/preferences.ts — Added SmartRoutingState interface & persistent storage methods for nanocoder-preferences.json
  • .changeset/smart-auto-routing.md — Added changeset for release automation

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Changeset

  • Added a changeset (pnpm changeset) describing this change for the changelog

Testing

Automated Tests

  • New features include passing tests in .spec.ts/tsx files
  • All existing tests pass (pnpm run test:types and pnpm run build complete with 0 errors)
  • Tests cover both success and error scenarios

16 new tests in source/ai-sdk-client/smart-router.spec.ts covering trivial prompts, code block detection, complex keywords, sensitivity thresholds, word boundary matching, custom options, and lightweight model auto-selection.

pnpm run test:types passes with 0 errors.

Manual Testing

  • Tested with Ollama
  • Tested with OpenRouter
  • Tested with OpenAI-compatible API
  • Tested MCP integration (if applicable)

Manually verified via CLI non-interactive and interactive TUI sessions:

  • Verified /smartroute on, /smartroute simple <model>, and /smartroute threshold <level> subcommands.
  • Verified persistence of "smartRouting" state across CLI process restarts in nanocoder-preferences.json.
  • Verified model selection stream output during chat execution.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Documentation updated (if needed)
  • No breaking changes (or clearly documented)
  • Appropriate logging added using structured logging

…ano-Collective#891)

- Add complexity classifier engine and AVA unit tests

- Add SmartRoutingState to config types and app state

- Add /smartroute slash command with on/off/simple/threshold subcommands

- Integrate per-turn model switching into chat handler

@akramcodez akramcodez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hey @atulya-srivastava, thanks for working on this! I went through the PR and the classifier itself looks really good - the fail-safe behavior, routing rules, and unit tests are all solid.

However, I don't think it's ready to merge yet. There are two important things I'd like you to address:

  1. Integration tests: The current 16 tests only cover the pure classifier. Please add coverage for the actual /smartroute handler, preference persistence, and the per-turn model swap/restore behavior. We need to make sure the feature actually works end-to-end, not just that the classifier works.

  2. Model state mutation: The current implementation temporarily mutates the shared client's model inside useChatHandler. I'd like us to discuss this approach before merging, particularly around concurrent submissions and ensuring the original model is always restored safely. If this approach is necessary, please document the invariants; otherwise, consider a safer way to pass the model to the LLM call.

Also, one small process note for future contributions: please ask for assignment on the issue before starting/opening a PR. It helps us coordinate contributors and avoid multiple people working on the same issue.

Once the integration coverage is added and we've discussed the model-state approach, we can take another look at this for review. The overall direction is good!

@atulya-srivastava

Copy link
Copy Markdown
Author

Hey @akramcodez ,

Thanks for the detailed feedback! glad to hear the classifier and routing logic look good.

I took another look at how useChatHandler, AISDKClient, and useAppState interact to better understand the concern about changing the model on the shared client.

Right now, turns from the interactive CLI are submitted one at a time while streaming. That said, I agree this is a UI level behavior rather than an architectural guarantee at the client layer.

Because of that, passing the model into each call is the safer approach. I propose adding an optional model?: string to ModeOverrides (passed into LLMClient.chat()) and having AISDKClient.chat() use it when provided, while keeping existing callers unchanged.

I will also add integration tests covering:

/smartroute behavior and preference persistence
• Choosing a model per turn for simple and complex prompts
• Model restoration and error handling
• Routing being disabled

This avoids relying on shared mutable state and keeps the change fairly contained. I am happy to update the PR this way. What's you view regarding this approach?

Also noted on asking for assignment before starting an issue. I will make sure to do that for future contributions.

Thanks again for the review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Smart Auto-Routing (Simple vs Strong Models)

2 participants