Local LLM Model Router: NeuralWatt routing service + context-aware framing and pruning #6

Merged
alee merged 3 commits from neuralwatt-router-service into main 2026-08-24 02:30:47 +00:00

3 Commits

Author SHA1 Message Date
adlee-was-taken
5f7716e121 fix: resolve context-pruning-and-framing review findings
Address the 11 confirmed bugs and 4 cleanups from
context-pruning-and-framing-review.md, plus the image-accounting
regression it found on a second pass.

classifier framing:
- _previous_context walks back to the nearest assistant turn only,
  skipping system/user/tool so raw tool output and the system prompt
  never contaminate the framing signal; reuses context_prune.extract_text
  instead of a duplicated block parser.
- classify() drops its redundant if/not-context branch.
- framing instruction moved out of the static system prompt and into
  _classifier_user_content so it travels only with content that justifies
  it (respects context_framing opt-out).

context pruning:
- extract_text counts image_url bytes so image-bearing tool results are
  sized (and pruned) correctly.
- -- new, follow-up review -- image_url blocks now count toward the size
  trigger but not toward the replacement text, and are stubbed to a short
  marker when the message is trimmed, so an image-heavy tool result
  genuinely shrinks rather than staying full-size while tokens_saved lied
  and base64 leaked into the text block.
- tokens_saved is now the honest whole-list before/after reduction
  (max(orig - final, 0)), never a per-message estimate that could drift.
- max_summarize_chars validated >= 3000 at config load; trim branch guards
  against negative math and message growth.
- recency guard protects the newest tool result even with no user turn.
- non-text blocks survive trimming (type preserved) rather than being
  flattened away.
- dropped/stat terminology corrected to summarized; _turn_of() removed;
  module-level pinch defaults removed in favor of PinchConfig.

Dispatch and config:
- prune_context runs once before the measured-context routing decision
  (reused at dispatch, no double-prune, no in-place mutation); no-op when
  pinch is disabled.
- dead context=prev_context arg removed from the measured reroute.
- TaskRequest.context dual use documented.

15 new regression tests pin the fixes; 545 tests pass.
2026-08-23 22:23:08 -04:00
adlee-was-taken
099958b392 docs: review + plan for context-pruning-and-framing feature 2026-08-23 22:22:56 -04:00
adlee-was-taken
fbcc63630a feat(classifier): context-aware framing and relevance-based context pruning
Port two ideas from the MIT-licensed alexrudloff/llmrouter:

1. Context-aware classification. chat_completions now feeds the classifier
   the message before the last user turn, framed as llmrouter's
   "Context: <prior>\n---\nMessage: <current>" (classifier.context_framing,
   default on). A short follow-up ("Yes", "Try now?") inherits the prior
   turn's complexity instead of being classified in isolation as trivial.
   _classifier_user_content is a pure, testable framing helper; the system
   prompt gains the inherit-from-context rule.

2. Relevance-based context pruning (context_prune.py). An optional,
   pre-dispatch stage that trims old tool results from the provider-bound
   conversation once it exceeds pinch.budget_tokens (pinch.enabled defaults
   off). User/assistant/system messages are always kept verbatim; only old
   tool results are summarized (head+tail with an elision marker) or dropped
   to a placeholder, with role pairing preserved so the result still parses
   as a conversation. Applies to both the streaming and non-streaming
   upstream bodies; the classifier input is exempt (it already clamps).

Tests: +23 (pruning invariants, framing, previous-context extraction, pinch
config validation). 529 pass.
2026-08-23 21:23:19 -04:00