Skip to main content

switchyard_libsy/algorithms/
passthrough.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Single-target routing for direct model calls and integration diagnostics.
5
6use std::sync::Arc;
7
8use switchyard_protocol::{Request, Response};
9
10use crate::Result;
11use crate::core::algorithm::{Algorithm, Driver, LlmTarget, RoutedRequest};
12use switchyard_protocol::{Context, Decision};
13
14/// Routing algorithm that always calls one configured target.
15pub struct Passthrough {
16    target: LlmTarget,
17}
18
19impl Passthrough {
20    /// Creates an algorithm that always calls `target`.
21    pub fn new(target: LlmTarget) -> Self {
22        Passthrough { target }
23    }
24}
25
26/// Decision emitted before [`Passthrough`] calls its configured target.
27pub struct PassthroughDecision {
28    model_id: String,
29}
30
31impl Decision for PassthroughDecision {
32    fn selected_model(&self) -> &str {
33        &self.model_id
34    }
35    fn reasoning(&self) -> Option<&str> {
36        // There is no decision to take, so no reasoning
37        None
38    }
39    fn as_any(&self) -> &dyn std::any::Any {
40        self
41    }
42}
43
44#[async_trait::async_trait]
45impl Algorithm for Passthrough {
46    fn name(&self) -> &str {
47        "passthrough"
48    }
49
50    async fn create_run_task(
51        self: Arc<Self>,
52        ctx: Context,
53        driver: Driver,
54        request: Request,
55    ) -> Result<Response> {
56        let decision: Arc<dyn Decision> = Arc::new(PassthroughDecision {
57            model_id: self.target.semantic_name.clone(),
58        });
59        driver.info(ctx.clone(), decision.clone()).await?;
60        driver
61            .call_llm(RoutedRequest {
62                request,
63                decision,
64                ctx,
65            })
66            .await
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use std::sync::Arc;
73
74    use super::Passthrough;
75    use crate::core::algorithm::{Algorithm, LlmTarget};
76    use crate::core::testing::{drive, echo};
77    use switchyard_protocol::{Context, Request, completion_text, text_request};
78
79    #[tokio::test]
80    async fn test_passthrough() -> crate::Result<()> {
81        const MODEL_ID: &str = "testing/passthrough";
82        let request = Request {
83            llm_request: text_request(Some("auto".to_string()), "hi"),
84            raw_request: None,
85            metadata: None,
86        };
87        let algorithm: Arc<dyn Algorithm> = Arc::new(Passthrough::new(LlmTarget {
88            semantic_name: MODEL_ID.to_string(),
89        }));
90        let (trace, response) = drive(algorithm, Context::default(), request, echo()).await?;
91
92        assert_eq!(
93            response
94                .llm_response
95                .as_agg()
96                .map(completion_text)
97                .unwrap_or_default(),
98            MODEL_ID
99        );
100        assert_eq!(trace.len(), 1);
101        assert_eq!(trace[0].selected_model(), MODEL_ID);
102        Ok(())
103    }
104}