Skip to main content

codetether_rlm/router/
should_route.rs

1//! Routing decision logic.
2
3use crate::capability::{OutputCapability, output_capability};
4use crate::chunker::RlmChunker;
5use crate::config::RlmConfig;
6
7use super::types::{RoutingContext, RoutingResult};
8
9/// Decide whether tool output should be routed through RLM.
10///
11/// Gated on [`output_capability`]: only
12/// [`OutputCapability::BulkSummarizable`] tools may be destructively
13/// summarised. `ExactContent` and `Unknown` fail closed.
14pub fn should_route(output: &str, ctx: &RoutingContext, config: &RlmConfig) -> RoutingResult {
15    let estimated = RlmChunker::estimate_tokens(output);
16
17    if config.mode == "off" {
18        return no_route("rlm_mode_off", estimated);
19    }
20
21    let cap = output_capability(ctx.tool_id.as_str());
22    if !matches!(cap, OutputCapability::BulkSummarizable) {
23        let reason = match cap {
24            OutputCapability::ExactContent => "tool_exact_content_no_route",
25            OutputCapability::Unknown => "tool_unknown_capability_no_route",
26            OutputCapability::BulkSummarizable => "tool_eligible",
27        };
28        return no_route(reason, estimated);
29    }
30
31    if config.mode == "always" {
32        return RoutingResult {
33            should_route: true,
34            reason: "rlm_mode_always".into(),
35            estimated_tokens: estimated,
36        };
37    }
38
39    let threshold = (ctx.model_context_limit as f64 * config.threshold) as usize;
40    if estimated > threshold {
41        return RoutingResult {
42            should_route: true,
43            reason: "exceeds_threshold".into(),
44            estimated_tokens: estimated,
45        };
46    }
47
48    overflow_check(ctx, estimated)
49}
50
51fn no_route(reason: &str, tokens: usize) -> RoutingResult {
52    RoutingResult {
53        should_route: false,
54        reason: reason.into(),
55        estimated_tokens: tokens,
56    }
57}
58
59fn overflow_check(ctx: &RoutingContext, estimated: usize) -> RoutingResult {
60    let Some(current) = ctx.current_context_tokens else {
61        return no_route("within_threshold", estimated);
62    };
63    let projected = current + estimated;
64    let limit = (ctx.model_context_limit as f64 * 0.8) as usize;
65    let dominates = estimated * 2 >= projected;
66    if projected > limit && dominates {
67        RoutingResult {
68            should_route: true,
69            reason: "would_overflow".into(),
70            estimated_tokens: estimated,
71        }
72    } else {
73        no_route("within_threshold", estimated)
74    }
75}