Skip to content

feat: Morph Model Router as an opt-in complexity classifier (#68) - #78

Merged
doramirdor merged 1 commit into
mainfrom
feat/morph-router-classifier
Jun 30, 2026
Merged

feat: Morph Model Router as an opt-in complexity classifier (#68)#78
doramirdor merged 1 commit into
mainfrom
feat/morph-router-classifier

Conversation

@doramirdor

Copy link
Copy Markdown
Collaborator

Closes #68.

Adds an optional, opt-in morph classifier that delegates the simple/mid/complex routing decision to Morph's hosted Model Router, built to the spec from the maintainer triage on #68.

What's in it

  • New NADIRCLAW_COMPLEXITY_ANALYZER=morphnadirclaw/morph_classifier.py with MorphRouterClassifier, wired into get_classifier() / warmup() dispatch alongside binary and distilbert.
  • Strictly opt-in. Default stays the local binary classifier. morph requires MORPH_API_KEY; a missing key degrades to binary at selection time (never a hard dependency for first-run UX).
  • Fail-closed to local. On missing key, HTTP error, timeout (>MORPH_TIMEOUT_MS, default 200ms), or an unparseable response, it logs once and serves that request from BinaryComplexityClassifier. A Morph outage degrades routing quality, never availability.
  • Latency budget + cache. MORPH_TIMEOUT_MS bounds the call; a small in-memory LRU keyed on prompt hash absorbs the repeated prompts a CLI session generates.
  • Consistent tiering. Morph's difficulty (easy/medium/hard/needs_info) maps to a complexity score, then to a tier via BinaryComplexityClassifier._score_to_tier, so the same NADIRCLAW_TIER_THRESHOLDS apply and mid only appears when NADIRCLAW_MID_MODEL is set.
  • No new dependency. HTTP via stdlib urllib.request, so it's trivially monkeypatched.

Config knobs

Env var Default Purpose
MORPH_API_KEY Required for morph; empty → fall back to binary.
MORPH_API_BASE https://api.morphllm.com/v1 Self-hosted/proxy override.
MORPH_TIMEOUT_MS 200 Per-request budget; past it, fail closed.

Tests

tests/test_morph_classifier.py — 15 cases covering the success path, all three fail-closed paths (HTTP error, timeout, bad JSON), response parsing (confidence vs 1 − ambiguity, nested classification, bad/unknown difficulty), caching, and the dispatch/degrade-without-key behavior. No live API call — the HTTP layer is monkeypatched. Full suite: 812 passed, 1 skipped locally.

Deferred (called out in the README)

The maintainer asked for cost-tracking honesty — subtracting the per-classification Morph fee from net saved in nadirclaw report. Morph's classification price isn't published in the issue, so that wiring is left as a follow-up and noted in the docs rather than guessed at.

Notes for review

  • The maintainer's triage suggested the env name NADIRCLAW_CLASSIFIER; the actual dispatch var in the codebase is NADIRCLAW_COMPLEXITY_ANALYZER, so I used that for consistency with binary/distilbert.
  • Morph's exact response schema isn't pinned in the issue, so _parse_response is defensive (flat or nested under classification; confidence else 1 − ambiguity else 1.0) and any surprise falls through the fail-closed path.

🤖 Generated with Claude Code

Adds NADIRCLAW_COMPLEXITY_ANALYZER=morph, delegating the simple/mid/complex
routing decision to Morph's hosted Model Router. Implemented per the maintainer
triage on #68:

- Strictly opt-in: default stays the local binary classifier; morph requires
  MORPH_API_KEY (missing key degrades to binary at selection time).
- Fail-closed to local: on missing key, HTTP error, timeout, or unparseable
  response, log once and serve that request from BinaryComplexityClassifier.
- Latency budget: MORPH_TIMEOUT_MS (default 200ms) + in-memory LRU on prompt
  hash for repeated CLI prompts.
- Tier mapping reuses BinaryComplexityClassifier._score_to_tier so the same
  NADIRCLAW_TIER_THRESHOLDS apply and `mid` only appears when MID_MODEL is set.
- Stdlib-only HTTP (urllib.request) — no new dependency; fully monkeypatched in
  tests (success, fail-closed, parsing, caching, dispatch). No live API in CI.

Cost-tracking honesty (subtracting the per-classification Morph fee from
net-saved in `nadirclaw report`) is deferred as a follow-up, noted in the README,
pending Morph's published classification price.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@doramirdor
doramirdor merged commit 6219808 into main Jun 30, 2026
3 checks passed
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.

Add Morph Model Router as a complexity classifier for routing decisions

1 participant