Skip to main content

switchyard_libsy/algorithms/vgr/
config.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::time::Duration;
5
6use switchyard_protocol::ModelId;
7
8use super::safety::{BreakerConfig, KillSwitch};
9use crate::{LibsyError, Result};
10
11/// Required attestation for serving efficient-tier answers in production.
12pub const ACTIVE_APPROVAL: &str = "prospective-validation-and-canary-approved";
13
14/// Authority granted to VGR decisions.
15#[derive(Clone, Debug, Default, Eq, PartialEq)]
16pub enum ServingMode {
17    /// Always serves the capable tier without producing an attempt.
18    #[default]
19    Off,
20    /// Serves decisions, for isolated measurement.
21    Evaluate,
22    /// Decides but always serves the capable tier.
23    Shadow,
24    /// Serves decisions after operator approval.
25    Active {
26        /// Operator approval attestation.
27        approval: String,
28    },
29}
30
31/// Targets used by a VGR route.
32#[derive(Clone, Debug)]
33pub struct Targets {
34    /// Tier that produces the candidate attempt.
35    pub efficient: ModelId,
36    /// Tier used when the candidate is not licensed.
37    pub capable: ModelId,
38    /// Efficient verifier, defaulting to `efficient`.
39    pub judge: Option<ModelId>,
40    /// Capable-tier verifier; unset removes the capable confirmation rungs.
41    pub capable_judge: Option<ModelId>,
42}
43
44/// VGR runtime configuration.
45#[derive(Clone, Debug)]
46pub struct VgrConfig {
47    /// Completion and verifier targets.
48    pub targets: Targets,
49    /// Authority granted to routing decisions.
50    pub mode: ServingMode,
51    /// Budget for one turn's attempt and verification.
52    pub deadline: Duration,
53    /// Enforces the decision deadline and efficient turn budget. Defaults to true.
54    pub bounded_efficient: bool,
55    /// Optional live stop controlled by the operator.
56    pub kill_switch: Option<KillSwitch>,
57    /// Efficient endpoint breaker tuning.
58    pub breaker: BreakerConfig,
59    /// Whether a cheap typing call selects a verification regime.
60    pub task_typing: bool,
61    /// Whether the efficient tier accepts image content.
62    pub efficient_supports_images: bool,
63    /// Lets a long agentic run with earlier tool errors commit on a capable-tier
64    /// confirmation once it ends in this many consecutive clean tool results.
65    pub confirmed_recovery_min_clean_tail: Option<u32>,
66    /// Tells the capable tier, once per user turn, that it inherits unverified
67    /// tool-using work.
68    pub agentic_handoff: bool,
69    /// Condenses history from the efficient tier at handoff. Requires `agentic_handoff`.
70    pub compact_handoff: bool,
71    /// Wall-clock budget for the efficient tier within one user turn, including the
72    /// client's tool execution.
73    pub efficient_turn_budget: Option<Duration>,
74    /// Holds an escalated session on the capable tier past later user turns.
75    /// Shadow mode retains only the current turn's hold.
76    pub latch_escalation: bool,
77}
78
79impl VgrConfig {
80    /// Creates an off-by-default route between efficient and capable tiers.
81    pub fn new(efficient: ModelId, capable: ModelId) -> Self {
82        Self {
83            targets: Targets {
84                efficient,
85                capable,
86                judge: None,
87                capable_judge: None,
88            },
89            mode: ServingMode::Off,
90            deadline: Duration::from_secs(30),
91            bounded_efficient: true,
92            kill_switch: None,
93            breaker: BreakerConfig::default(),
94            task_typing: true,
95            efficient_supports_images: false,
96            confirmed_recovery_min_clean_tail: None,
97            agentic_handoff: false,
98            compact_handoff: false,
99            efficient_turn_budget: None,
100            latch_escalation: false,
101        }
102    }
103
104    pub(super) fn validate(&self) -> Result<()> {
105        if self.compact_handoff && !self.agentic_handoff {
106            return Err(LibsyError::AlgorithmError {
107                message: "compact_handoff requires agentic_handoff".into(),
108            });
109        }
110        if self.targets.efficient == self.targets.capable {
111            return Err(LibsyError::AlgorithmError {
112                message: "vgr requires distinct efficient and capable model IDs".into(),
113            });
114        }
115        if self.deadline.is_zero() || self.breaker.threshold == 0 {
116            return Err(LibsyError::AlgorithmError {
117                message: "vgr deadline and breaker threshold must be non-zero".into(),
118            });
119        }
120        if let ServingMode::Active { approval } = &self.mode
121            && approval != ACTIVE_APPROVAL
122        {
123            return Err(LibsyError::AlgorithmError {
124                message: format!(
125                    "vgr active mode requires approval attestation {ACTIVE_APPROVAL:?}"
126                ),
127            });
128        }
129        Ok(())
130    }
131
132    pub(super) fn judge(&self) -> &ModelId {
133        self.targets
134            .judge
135            .as_ref()
136            .unwrap_or(&self.targets.efficient)
137    }
138
139    pub(super) fn confirmed_min_clean_tail(&self) -> Option<i32> {
140        self.confirmed_recovery_min_clean_tail
141            .map(|tail| i32::try_from(tail.max(1)).unwrap_or(i32::MAX))
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn compaction_requires_handoff_for_direct_callers() {
151        let mut config = VgrConfig::new("efficient".into(), "capable".into());
152        assert!(!config.compact_handoff);
153        config.compact_handoff = true;
154        assert!(super::super::Vgr::new(config.clone()).is_err());
155        config.agentic_handoff = true;
156        assert!(super::super::Vgr::new(config).is_ok());
157    }
158
159    #[test]
160    fn direct_callers_must_use_distinct_efficient_and_capable_ids() {
161        assert!(super::super::Vgr::new(VgrConfig::new("same".into(), "same".into())).is_err());
162        assert!(
163            super::super::Vgr::new(VgrConfig::new("efficient".into(), "capable".into())).is_ok()
164        );
165    }
166}