jarvis ask never routes: score_complexity() is used only for max_tokens, select_model() is never called

Author: tim703223-glitchCreated Sep 4, 2026Updated Sep 16, 2026

Description

jarvis ask never performs model routing. It computes a complexity score for every query and then uses that score only to size max_tokens -- the model and engine come from config or flags, and select_model() is never called anywhere in the path.

The result is that the documented local-first / cloud-escalation behaviour does not happen through the primary entry point. A trivial query and a hard reasoning query both run on whatever single model is configured.

Where

cli/ask.py:

python
from openjarvis.learning.routing.complexity import (
    ComplexityResult,
    adjust_tokens_for_model,
    score_complexity,
)

complexity_result: ComplexityResult = score_complexity(query_text)
logger.debug(
    "Complexity analysis: score=%.3f tier=%s suggested_max_tokens=%d",
    complexity_result.score, complexity_result.tier,
    complexity_result.suggested_max_tokens,
)
...
selection_model = model_name or config.intelligence.default_model or None
resolved = get_engine(config, effective_engine_key, model=selection_model)

The score is logged at debug level and used for token budgeting. RouterPolicyRegistry, HeuristicRouter and select_model do not appear in this path.

Steps to Reproduce

  1. Configure a small local model and a larger cloud model.
  2. jarvis ask "hi"
  3. jarvis ask "Prove that the sum of the first n odd integers equals n squared."
  4. Both run on intelligence.default_model. No escalation, no log line indicating a routing decision.

Expected Behavior

Per docs/architecture/learning.md, the Learning primitive "determines which model handles each query (router policies)". A user who configures [learning.routing] policy = "heuristic" would reasonably expect jarvis ask to honour it.

Actual Behavior

[learning.routing] has no effect on jarvis ask. Routing is configurable but inert.

Notes

The routing machinery itself works. Driving HeuristicRouter directly with a real two-model list produces correct decisions:

LOCAL  llama3.2:3b   <- "hi"
CLOUD  <120b model>  <- "Prove that the sum of the first n odd integers..."
CLOUD  <120b model>  <- "Write a Python function to parse nested JSON."

So this is a wiring gap, not a logic defect. Two related issues compound it: #942 (cloud models cannot win _largest_model because parameter_count_b is 0.0), and the executor issue filed alongside this one (the router is constructed with a single-element model list).

Suggested fix

In ask.py, when config.learning.routing.policy is set, build the candidate list from the available engines' models, create the policy via RouterPolicyRegistry, and let it choose -- falling back to default_model when it returns nothing. Logging the chosen model and the reason would also make the behaviour observable, which it currently is not.

Version

1.0.4.dev210+gc1af3c94 -- Windows 11, Python 3.12.10, ollama + cloud engines


Found while setting up local-first routing with cloud escalation; investigated with Claude Code.