Skip to main content

switchyard_libsy/algorithms/
noop.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Test-only algorithm that returns a hard-coded response without calling a backend.
5
6use std::sync::Arc;
7
8use switchyard_protocol::{
9    AggLlmResponse, ContentBlock, LlmResponse, Request, Response, ResponseOutput, Role, StopReason,
10};
11
12use crate::Result;
13use crate::core::algorithm::{Algorithm, Driver};
14use switchyard_protocol::{Context, Decision};
15
16/// Test helper that returns a hard-coded response without routing or model I/O.
17pub struct Noop {}
18
19/// Test decision carrying the inbound model or a fixed placeholder.
20pub struct NoopDecision {
21    model: String,
22}
23
24impl Decision for NoopDecision {
25    fn selected_model(&self) -> &str {
26        &self.model
27    }
28    fn reasoning(&self) -> Option<&str> {
29        None
30    }
31    fn as_any(&self) -> &dyn std::any::Any {
32        self
33    }
34}
35
36#[async_trait::async_trait]
37impl Algorithm for Noop {
38    fn name(&self) -> &str {
39        "noop"
40    }
41
42    async fn create_run_task(
43        self: Arc<Self>,
44        ctx: Context,
45        driver: Driver,
46        request: Request,
47    ) -> Result<Response> {
48        let model = request
49            .requested_model()
50            .unwrap_or("switchyard/noop")
51            .to_string();
52        let decision: Arc<dyn Decision> = Arc::new(NoopDecision {
53            model: model.clone(),
54        });
55        driver.info(ctx, decision.clone()).await?;
56
57        let llm_response = LlmResponse::Agg(AggLlmResponse {
58            id: Some("switchyard-noop".to_string()),
59            model: Some(model),
60            outputs: vec![ResponseOutput {
61                role: Role::Assistant,
62                content: vec![ContentBlock::Text {
63                    text: "OK".to_string(),
64                }],
65                stop_reason: Some(StopReason::EndTurn),
66            }],
67            ..Default::default()
68        });
69        let response = Response {
70            llm_response,
71            metadata: request.metadata.clone(),
72        };
73        Ok(response)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use switchyard_protocol::{LlmRequest, Message, Role};
80
81    use super::*;
82    use crate::core::testing::{drive, echo};
83
84    #[tokio::test]
85    async fn test_noop_algo() -> Result<()> {
86        const TEST_MODEL: &str = "test_noop_algo";
87        let request = Request {
88            llm_request: LlmRequest {
89                model: Some(TEST_MODEL.to_string()),
90                messages: vec![Message::text(Role::User, "hi")],
91                ..LlmRequest::default()
92            },
93            raw_request: None,
94            metadata: None,
95        };
96
97        // `Noop` synthesizes its own response and never offloads a call, so `echo` is
98        // never reached.
99        let a: Arc<dyn Algorithm> = Arc::new(Noop {});
100        let (decisions, response) = drive(a, Context::default(), request, echo()).await?;
101        let Some(decision) = decisions.first() else {
102            panic!("Expected exactly one Decision");
103        };
104        assert_eq!(decision.selected_model(), TEST_MODEL);
105        assert_eq!(response.selected_model(), Some(TEST_MODEL));
106        Ok(())
107    }
108}